PakageManagerService的启动流程图
1.PakageManagerService概述
PakageManagerService是android系统中一个核心的服务,它负责系统中Package的管理,应该程序的安装、卸载等。后面PakageManagerService简称PMS。
2.SystemServer启动PackageManagerService
我之前的ATA文章有说到,SystemServer进程是Zygote孵化出的第一个进程,该进程主要的工作是启动android系统服务进程,其中包括PackageManagerService服务,SystemServer启动PMS关键源码如下:
private void startBootstrapServices() {
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
}
关键点
3.PMS的main方法
PackageManagerService的主要功能是,扫描Android系统中几个目标文件夹的APK,建立对应的数据结构来管理Package信息、四大组件信息、权限信息等各种信息。例如PKMS解析APK包中的AndroidMainfest.xml,并根据其中声明的Activity标签来创建对应的对象并加以保管。PMS的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;
}
该方法看似很简单,只有几行代码,然而执行事件却比较长,这是因为PMS在其构造函数中做了很多的“重体力活”,这也是android启动速度慢的主要因素之一。安装的应用越多,系统启动开机时间越长。
PMS构造函数的主要工作流程
- 扫描目标文件夹之前的准备工作。
- 扫描目标文件夹。
- 扫描之后的工作。
4.PMS的前期准备工作
4.1探究Setting
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;
mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
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);
4.1.1Android系统中UID/GID
UID为用户ID的缩写,GID为用户组ID的缩写,这两个概念均与Linux系统中进程的权限管理有关。一般来说,每一个进程都有一个对应的UID,表示该进程属于哪个用户,不同用户有不同权限。一个进程也可分属不同的用户组,每个用户组都有对应的权限。
在android系统中,系统定义的UID/GID在Process.java文件中,关键源码如下所示
public static final int SYSTEM_UID = 1000;
public static final int PHONE_UID = 1001;
public static final int SHELL_UID = 2000;
public static final int LOG_UID = 1007;
public static final int WIFI_UID = 1010;
public static final int MEDIA_UID = 1013;
public static final int DRM_UID = 1019;
public static final int VPN_UID = 1016;
public static final int NFC_UID = 1027;
public static final int BLUETOOTH_UID = 1002;
public static final int MEDIA_RW_GID = 1023;
public static final int PACKAGE_INFO_GID = 1032;
public static final int SHARED_RELRO_UID = 1037;
public static final int FIRST_APPLICATION_UID = 10000;
public static final int LAST_APPLICATION_UID = 19999;
public static final int FIRST_ISOLATED_UID = 99000;
public static final int LAST_ISOLATED_UID = 99999;
4.1.2 探究SharedUserSetting
Setting中有一个mShareUsers成员,该成员存储的是字符串变量name与SharedUserSetting健值对。
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
if (s.userId == uid) {
return s;
}
return null;
}
创建一个SharedUserSetting对象,并设置为userid为uid
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
s.userId = uid;
if (addUserIdLPw(uid, s, name)) {
mSharedUsers.put(name, s);
return s;
}
return null;
}
例如在SystemUI.apk的AndroidManifest.xml文件中,有关键代码:
<mainfest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.systemui"
coreApp="true"
android:sharedUserId="android.uid.system"
android:process="system">
....
在该标签中,声明了一个android:sharedUserId的属性,其值为“android.uid.system”。sharedUserId和UID有关,它的作用是
- 两个或者多个声明了同一种sharedUserid的APK可共享彼此的数据,并且可运行在同一进程中。
- 通过声明特定的sharedUserId,该APK所在的进程将被赋予指定UID。
例如SystemUI声明了system的uid,运行SystemUI的进程就可享有system用户所对应的权限了,实际上就是将该进程的UID设置为system的uid了
接下来分析addUserIdLPw的功能,它主要就是将SharedUserSettings对象保存到对应的数组中,代码如下
private boolean addUserIdLPw(int uid, Object obj, Object name) {
if (uid > Process.LAST_APPLICATION_UID) {
return false;
}
if (uid >= Process.FIRST_APPLICATION_UID) {
int N = mUserIds.size();
final int index = uid - Process.FIRST_APPLICATION_UID;
while (index >= N) {
mUserIds.add(null);
N++;
}
if (mUserIds.get(index) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate user id: " + uid
+ " name=" + name);
return false;
}
mUserIds.set(index, obj);
} else {
if (mOtherUserIds.get(uid) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared id: " + uid
+ " name=" + name);
return false;
}
mOtherUserIds.put(uid, obj);
}
return true;
}
4.2 XML文件扫描
接下来是扫描系统目录下与系统权限相关的xml文件,将其存放到PKM中,关键源码如下:
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
synchronized (mInstallLock) {
synchronized (mPackages) {
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true );
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
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");
sUserManager = new UserManagerService(context, this,
mInstallLock, mPackages);
ArrayMap<String, SystemConfig.PermissionEntry> permConfig
= systemConfig.getPermissions();
for (int i=0; i<permConfig.size(); i++) {
SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
BasePermission bp = mSettings.mPermissions.get(perm.name);
if (bp == null) {
bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
mSettings.mPermissions.put(perm.name, bp);
}
if (perm.gids != null) {
bp.setGids(perm.gids, perm.perUser);
}
}
ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
for (int i=0; i<libConfig.size(); i++) {
mSharedLibraries.put(libConfig.keyAt(i),
new SharedLibraryEntry(libConfig.valueAt(i), null));
}
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
mSdkVersion, mOnlyCore);
String customResolverActivity = Resources.getSystem().getString(
R.string.config_customResolverActivity);
if (TextUtils.isEmpty(customResolverActivity)) {
customResolverActivity = null;
} else {
mCustomResolverComponentName = ComponentName.unflattenFromString(
customResolverActivity);
}
long startTime = SystemClock.uptimeMillis();
进一步我们再观察SystemConfig是如何解析系统权限xml文件的,在SystemConfig的构造函数中,它会去分别读取etc目录下的sysconfig,permissions,sysconfig目录下的文件。
SystemConfig() {
// Read configuration from system
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "sysconfig"), false);
// Read configuration from the old permissions dir
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions"), false);
// Only read features from OEM config
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "sysconfig"), true);
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "permissions"), true);
}
我们看看到底这些目录下放着什么样的文件,例如/etc/permissions目录下的文件如下图:
我们再打开第一个文件来探究,没错,这个文件代表蓝牙权限,表示该设备支持蓝牙。具体代码如下
<?xml version="1.0" encoding="utf-8"?>
<permissions>
<feature name="android.hardware.bluetooth" />
</permissions>
总结一下PMS的前期工作,其实就是扫描并解析XML文件,将其中的信息保存到特定的数据结构中。
5.PMS扫描Package
第二个阶段的工作主要是扫描系统中的APK,由于需要逐个扫描apk文件,因此手机上安装的程序越多,PKM的工作量越大,系统启动速度越慢,也就是开机时间越长。
5.1系统库的dex优化
以下的代码主要是对系统库BOOTCLASSPATH指定,或platform.xml定义,或者/system/frameworks目录下的jar
和apk包进行一次检查,该dex优化的优化.dex优化后会在相应的目录生成.odex文件。/system/frameworks如下图:
关键源码如下:
final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;
final ArraySet<String> alreadyDexOpted = new ArraySet<String>();
final String bootClassPath = System.getenv("BOOTCLASSPATH");
final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
if (bootClassPath != null) {
String[] bootClassPathElements = splitString(bootClassPath, ':');
for (String element : bootClassPathElements) {
alreadyDexOpted.add(element);
}
}
if (mSharedLibraries.size() > 0) {
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
alreadyDexOpted.add(lib);
mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
}
}
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
String[] frameworkFiles = frameworkDir.list();
if (frameworkFiles != null) {
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (int i=0; i<frameworkFiles.length; i++) {
File libPath = new File(frameworkDir, frameworkFiles[i]);
String path = libPath.getPath();
if (alreadyDexOpted.contains(path)) {
continue;
}
if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
continue;
}
int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
}
}
}
}
5.2扫描系统的APK
对apk或者jar进行dex优化后,现在PKM进入了重点阶段,扫描系统的APK,每一个APK对应一个Package对象,主要是扫描APK的AndroidManifest.xml,解析application标签及其子标签actvity、service、recever等,也就是android的四大组件,解析后将它们保存到Package对应的数据结构中,最后将它们注册到PKM中,要扫描以下几个目录:
- /system/frameworks:该目录下的文件都是系统库,例如service.jar、framework.jar、framework-res.apk。不过只扫描framework-res.apk文件
- /system/app:该目录下全是默认的系统应用和厂商特定的APK文件,例如Buletooth.apk、和SystemUI.apk等
解析AndroidManifest.xml关键的源码如下:
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
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] = "<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, attrs, flags, outError)) {
return null;
}
} else if (tagName.equals("overlay")) {
} else if (tagName.equals("key-sets")) {
} 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-permission-sdk-m")
|| tagName.equals("uses-permission-sdk-23")) {
} else if (tagName.equals("uses-configuration")) {
} else if (tagName.equals("uses-feature")) {
} else if (tagName.equals("feature-group")) {
} else if (tagName.equals("uses-sdk")) {
} else if (tagName.equals("supports-screens")) {
} 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("supports-input")) {
} else if (tagName.equals("eat-comment")) {
} else if (RIGID_PARSER) {
} else {
}
}
}
具体看一下怎么解析application标签下的四大组件的,依次解析activity,receiver,service,provider,其中可以发现,receiver被当成activity来解析了,PKM通过PackageParser类将解析后的四大组件保存到对应数据结构中,也就是存放到PackageParser的activities,receivers,providers,services对象中。关键源码如下:
private boolean parseBaseApplication(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError){
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, attrs, flags, outError, false,
owner.baseHardwareAccelerated);
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.receivers.add(a);
//service标签
} else if (tagName.equals("service")) {
Service s = parseService(owner, res, parser, attrs, flags, outError);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.services.add(s);
//provider标签
} else if (tagName.equals("provider")) {
Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
} else if (tagName.equals("activity-alias")) {
} else if (parser.getName().equals("meta-data")) {
} else if (tagName.equals("uses-library")) {
} else if (tagName.equals("uses-package")) {
}
}
}
在PackageParser扫描完一个APK后,此时系统已经根据APK中的AndroidMainifest.xml,创建了一个Package对象,下一步是将该Package加入到系统中。此时调用scanPackageDirtyLI方法,scanPackageDirtyLI首先会对packageName为“android”的apk做单独的处理,该apk其实就是framework-res.apk,它包含了几个常见的activity
- ChooserActivity:当startActivity有多个Acitvity符合时,系统会弹出此Acitivity,由用户选择合适的应用来处理
- ShutDownActivity:关机前弹出的选择对话框
- RingtonePickerAcitivity:铃声选择Activity
scanPackageDirtyLI关键代码如下:
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
final File scanFile = new File(pkg.codePath);
if (pkg.applicationInfo.getCodePath() == null ||
pkg.applicationInfo.getResourcePath() == null) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Code and resource paths haven't been set correctly");
}
if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
} else {
pkg.coreApp = false;
}
if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
if (mCustomResolverComponentName != null &&
mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
setUpCustomResolverActivity(pkg);
}
if (pkg.packageName.equals("android")) {
synchronized (mPackages) {
if (mAndroidApplication != null) {
}
mPlatformPackage = pkg;
pkg.mVersionCode = mSdkVersion;
mAndroidApplication = pkg.applicationInfo;
if (!mResolverReplaced) {
mResolveActivity.applicationInfo = mAndroidApplication;
mResolveActivity.name = ResolverActivity.class.getName();
mResolveActivity.packageName = mAndroidApplication.packageName;
mResolveActivity.processName = "system:ui";
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;
mResolveActivity.exported = true;
mResolveActivity.enabled = true;
mResolveInfo.activityInfo = mResolveActivity;
mResolveInfo.priority = 0;
mResolveInfo.preferredOrder = 0;
mResolveInfo.match = 0;
mResolveComponentName = new ComponentName(
mAndroidApplication.packageName, mResolveActivity.name);
}
}
}
}
“android“该Package与系统有非常重要的作用,这里保存特殊处理保存该Package的信息,主要是为了提高运行过程中的效率,例如ChooserActivity使用的地方非常多。
接下里scanPackageDirtyLI方法会对系统其它的Package做处理,关键源码如下:
if (mPackages.containsKey(pkg.packageName)
|| mSharedLibraries.containsKey(pkg.packageName)) {
}
if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
if (mExpectingBetter.containsKey(pkg.packageName)) {
logCriticalInfo(Log.WARN,
"Relax SCAN_REQUIRE_KNOWN requirement for package " + pkg.packageName);
} else {
PackageSetting known = mSettings.peekPackageLPr(pkg.packageName);
if (known != null) {
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Examining " + pkg.codePath
+ " and requiring known paths " + known.codePathString
+ " & " + known.resourcePathString);
}
if (!pkg.applicationInfo.getCodePath().equals(known.codePathString)
|| !pkg.applicationInfo.getResourcePath().equals(known.resourcePathString)) {
throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
"Application package " + pkg.packageName
+ " found at " + pkg.applicationInfo.getCodePath()
+ " but expected at " + known.codePathString + "; ignoring.");
}
}
}
}
File destCodeFile = new File(pkg.applicationInfo.getCodePath());
File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
SharedUserSetting suid = null;
PackageSetting pkgSetting = null;
if (!isSystemApp(pkg)) {
pkg.mOriginalPackages = null;
pkg.mRealPackage = null;
pkg.mAdoptPermissions = null;
}
final String pkgName = pkg.packageName;
final long scanFileTime = scanFile.lastModified();
final boolean forceDex = (scanFlags & SCAN_FORCE_DEX) != 0;
pkg.applicationInfo.processName = fixProcessName(
pkg.applicationInfo.packageName,
pkg.applicationInfo.processName,
pkg.applicationInfo.uid);
File dataPath;
if (mPlatformPackage == pkg) {
dataPath = new File(Environment.getDataDirectory(), "system");
pkg.applicationInfo.dataDir = dataPath.getPath();
} else {
dataPath = Environment.getDataUserPackageDirectory(pkg.volumeUuid,
UserHandle.USER_OWNER, pkg.packageName);
boolean uidError = false;
if (dataPath.exists()) {
} else {
if (DEBUG_PACKAGE_SCANNING) {
if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
Log.v(TAG, "Want this data dir: " + dataPath);
}
int ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.seinfo);
if (ret < 0) {
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
"Unable to create data dirs [errorCode=" + ret + "]");
}
}
pkgSetting.uidError = uidError;
}
final String path = scanFile.getPath();
final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
derivePackageAbi(pkg, scanFile, cpuAbiOverride, true );
if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
pkg.applicationInfo.primaryCpuAbi == null) {
setBundledAppAbisAndRoots(pkg, pkgSetting);
setNativeLibraryPaths(pkg);
}
} else {
if ((scanFlags & SCAN_MOVE) != 0) {
setNativeLibraryPaths(pkg);
}
if ((scanFlags & SCAN_NO_DEX) == 0) {
int result = mPackageDexOptimizer.performDexOpt(pkg, null ,
forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false );
if ((scanFlags & SCAN_REPLACING) != 0) {
killApplication(pkg.applicationInfo.packageName,
pkg.applicationInfo.uid, "replace pkg");
}
synchronized (mPackages) {
...
int N = pkg.providers.size();
StringBuilder r = null;
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);
mProviders.addProvider(p);
p.syncable = p.info.isSyncable;
if (p.info.authority != null) {
}
N = pkg.services.size();
r = null;
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);
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(s.info.name);
}
}
if (r != null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Services: " + r);
}
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");
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(a.info.name);
}
}
if (r != null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Receivers: " + r);
}
N = pkg.activities.size();
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 ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(a.info.name);
}
}
if (r != null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Activities: " + r);
}
return pkg;
}
5.3扫描非系统apk
在PackageManagerService构造函数扫描完系统apk后,接下来就是扫描非系统apk,这些apk在/data/app或者/data/app-private中。如下图:
下面是关键源码,scanDirLI已经在前面分析过了。跟系统apk的调用过程差不多。
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
}
5.4扫描结果保存到文件中
在PackageManagerService构造函数收尾阶段,PMS将前面收集的信息再整理一次,将已安装的apk信息写到package.xml、pacakage.list和package-stopped.xml中
updatePermissionsLPw(null, null, updateFlags);
mSettings.writeLPr();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
mRequiredVerifierPackage = getRequiredVerifierLPr();
mRequiredInstallerPackage = getRequiredInstallerLPr();
}
}
Runtime.getRuntime().gc();
- packages.xml:系统对程序安装,卸载和更新等操作时会更新该文件,PMS扫描完目标文件夹后创建该文件,保存了Package相关的信息。
- packages.list:保存着系统中所有的非系统自带的APK信息,程序安装,卸载和更新会更新该文件。
- packages-stoped.xml:保存系统中被用户强制停止的Package的信息。
5.4扫描系统和非系统apk总结
PKM在这个过程中工作任务非常繁重,要创建很多的对象,所以它是一个耗时耗内存的操作,从流程来看,PKM在这个过程中无非是扫描XML或者APK文件,但是其中涉及的数据结构及它们的关系较为复杂。
作者:xiangzhihong8 发表于2016/10/14 11:41:51
原文链接