目录如上所示~~↑↑↑
PMS入口
PMS从/frameworks/base/services/java/com/android/server/SystemServer.java中的startBootstrapServices()启动
private void startBootstrapServices(){
Installer installer = mSystemServiceManager.startService(Installer.class);
// Install是pm路径下面的一个单独的类,主要用于通过InstallerConnection建立和installd的链接
// 然后Installd会进行创建一些系统关键目录的作用,所以我们要等待Installd的结束,才可以继续进行其它的创建
...
// Start the package manager.
Slog.i(TAG, "Package Manager");
mPackageManagerService = PackageManagerService.main(mSystemContext, installer, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
//第二个参数installer负责和native层中的installd守护进程进行socket通信。
//第四个参数mOnlyCore用于判断是否仅仅扫描系统的目录,只有在与data分区加解密时才会设置为true,其他情况一般都为false。
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
...
}
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;
}
main函数主要做了两件事:
1.创建PackageManagerService对象2.将PMS向SMS中注册,即加入SMS中,方便后续其他进程或者app通过SMS获得PMS服务
时序图如下:
PMS构造函数分析
PMS构造函数的工作,总结起来就是扫描Android系统中的apk,并且建立相应的数据结构去管理Package的信息,四大组件的信息,权限信息等内容。
1.获取系统默认配置
主要获取ro.build.type和debug.separate_processes两个参数
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis());
if (mSdkVersion <= 0) { //先去检查rom的sdk版本,让apk知道运行于哪个版本
Slog.w(TAG, "**** ro.build.version.sdk not set!");
}
mContext = context;
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
//ro.build.type有三种:user,userdebug,eng。连接机器后可以通过 getprop ro.build.type 来查看
long dexOptLRUThresholdInMinutes;
if (mLazyDexOpt) {//如果为eng,则mLazyDexOpt为true,只对30分钟内所使用的APP做优化。
dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds.
} else {
dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users.
}
mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;
//SystemServer.startOtherServices()->PkMS.performBootDexOpt()->filterRecentlyUsedApps()里用到
mMetrics = new DisplayMetrics();
//new一个DisplayMetrics对象,用于保存屏幕像素参数,匹配APK中的asset和resource
String separateProcesses = SystemProperties.get("debug.separate_processes");
...
//debug.separate_processes:用于标记是否在独立进程中运行某个程序。
//根据该值设置PkMS.mDefParseFlags和PkMS.mSeparateProcesses两个全局变量,后续scanDirLi扫描并安装APK时用到。
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;
}
...
}
2.创建Settings对象并添加共享ID
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
...
mSettings = new Settings(context);// ---见2.1
//new一个Settings对象,这个settings是pm里面的,主要是保存系统apk的相关设置,互相之间关系等内容
//下面是创建6个共享uid
// ---见2.2、2.3
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
...
}
2.1Setting分析
源码路径:/frameworks/base/services/core/java/com/android/server/pm/Settings.java
创建了/data/system的目录,创建了如package.xml等5个文件,设置权限。构造函数如下:
Settings(Object lock) {
this(Environment.getDataDirectory(), lock);
}
Settings(File dataDir, Object lock) {
mLock = lock;
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
mSystemDir = new File(dataDir, "system");
// dataDir为/data mSystemDir为/data/system
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
mSettingsFilename = new File(mSystemDir, "packages.xml");//保存了系统所有的Package信息
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");//packages.xml的备份,防止在写packages.xml突然断电
mPackageListFilename = new File(mSystemDir, "packages.list");//保存了系统中已经安装的apk,以及对应的data/data/下面的对应关系
FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
// Deprecated: Needed for migration
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");//用于记录系统中强制停止运行的Package信息
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
//packages-stopped.xml的备份,防止在写packages-stopped-backup的时候突然断电
}
2.2 UID
UID是User ID的缩写,系统定义的UID/GID在/frameworks/base/core/java/android/os/Process.java文件中,部分如下:
public static final int VPN_UID = 1016;
public static final int NFC_UID = 1027;
public static final int BLUETOOTH_UID = 1002;
2.3 addSharedUserLPw
addSharedUserLPw方法,主要是创建共享UID的相关信息:
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
SharedUserSetting s = mSharedUsers.get(name);
//先以name为key,在mSharedUsers中查看找是否已经有名为name的共享UID
if (s != null) {//找得到
if (s.userId == uid) {//判断此UID和传入的uid是否相等
return s;
}
PackageManagerService.reportSettingsProblem(Log.ERROR, "Adding duplicate shared user, keeping first: " + name);
//不相等就报错,即不能对已有的共享UID信息,绑定新的uid。
return null;
}
//没找到,就new一个SharedUserSetting,并将其加入mSharedUsers中去。
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
//pkgPrivateFlags为ApplicationInfo.PRIVATE_FLAG_PRIVILEGED表明具备system权限。
//pkgFlags为ApplicationInfo.FLAG_SYSTEM表明该app是system app。
s.userId = uid;
if (addUserIdLPw(uid, s, name)) { // ---见2.3.1
mSharedUsers.put(name, s);
return s;
}
return null;
}
mSharedUsers是一个hashmap,HashMap<String, SharedUserSetting>。
其是SharedUserSetting类的实例,该类继承自GrantedPermissions,包含了如下的五个内部变量:
final String name;
int userId;
// flags that are associated with this uid, regardless of any package flags
int uidFlags;
final HashSet<PackageSetting> packages = new HashSet<PackageSetting>();
final PackageSignatures signatures = new PackageSignatures();
其中第四个变量PackageSetting继承自PackageSettingBase,其主要有下面的三个成员变量:
int appId;
PackageParser.Package pkg;
SharedUserSetting sharedUser;
2.3.1 addUserIdLPw方法
private boolean addUserIdLPw(int uid, Object obj, Object name) {
if (uid > Process.LAST_APPLICATION_UID) {
// 系统为app分配的UID为10000 - 19999,如果超出了这个范围就会报错
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;
}
android为每一个app都分配了一个UID,每个app从Linux的层面上面,都是一个独立的用户。
该方法主要针对普通uid和system uid进行不同处理。普通的app的uid加入mUsrIds,其他的加入mOtherUserIds。
mUserIds是一个ArrayList,也就是一个动态的数组
private final ArrayList<Object> mUserIds = new ArrayList<Object>();
3.创建dexopt优化器对象
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
...
mInstaller = installer;//是与installd守护进程通信的installer。
mPackageDexOptimizer = new PackageDexOptimizer(this);
//创建PackageDexOptimizer对象,该类主要用来执行ART中的patchoat命令,用来对oat文件的偏移值进行随机化。该类是Android M 中才有的。
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(FgThread.get().getLooper());
//创建监听权限更显的监听者。因为Android M中允许动态修改App权限。
...
}
4.解析系统Permission
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
...
SystemConfig systemConfig = SystemConfig.getInstance();//单例模式,获取SystemConfig的实例
mGlobalGids = systemConfig.getGlobalGids();//系统的etc的xml下面所声明的所有的gid
mSystemPermissions = systemConfig.getSystemPermissions();//系统中etc下xml声明的uid和其对应的权限
mAvailableFeatures = systemConfig.getAvailableFeatures();//系统中硬件所拥有的feature
......
}
SystemConfig会读取/system/etc/permissions文件夹中的相关文件,
其位于目录/frameworks/base/services/core/java/com/android/server/SystemConfig.java ,构造方法如下:
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);
}
其中rootDirectory是“/system”.oemDirectory是”/oem”.也就是会尝试依次读取下面四个目录中的xml文件.
/system/etc/sysconfig (无)
/system/etc/permissions (很多 15个 都是编译时从frameworks/native/data/etc/拷贝的)
/oem/etc/sysconfig (无 因为oem目录下为空)
/oem/etc/permissions
system下的两个文件读取完后, 会在5.3小节的步骤中进行相关操作。(此句好像不对)
permission文件的读取是通过readPermissions函数完成的:
void readPermissions(File libraryDir, boolean onlyFeatures) {
// Read permissions from given directory.
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
if (!onlyFeatures) {
Slog.w(TAG, "No directory " + libraryDir + ", skipping");
}
return;//如果文件夹不存在,或者其不是一个文件夹,退出
}
if (!libraryDir.canRead()) {
Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
return;//如果不可读,则退出
}
// Iterate over the files in the directory and scan .xml files
File platformFile = null;
for (File f : libraryDir.listFiles()) {
if (f.getPath().endsWith("etc/permissions/platform.xml")) {
platformFile = f;
continue;//先不处理platform.xml,最后会单独处理
}
if (!f.getPath().endsWith(".xml")) {
Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
continue;//只处理xml文件
}
if (!f.canRead()) {
Slog.w(TAG, "Permissions library file " + f + " cannot be read");
continue;//如果xml文件不可读,跳过
}
readPermissionsFromXml(f, onlyFeatures);//解析xml文件
}
// Read platform permissions last so it will take precedence
if (platformFile != null) {
//最后单独处理platform.xml文件
readPermissionsFromXml(platformFile, onlyFeatures);
}
}
readPermissions方法的作用就是读取指定目录下的xml文件,然后调用readPermissionsFromXml方法来解析xml文件中的permission内容,保存在不同的数据结构中。
其中xml涉及到的标签内容有feature、library、permission、assign-permission等,这些标签的内容都将解析出来保存到SystemConfig的对应数据结构的全局变量中以便以后查询管理。festure标签用来描述设备是否支持硬件特性;library用于指定系统库,当应用程序运行时,系统会为进程加载一些必要的库,permission用于将permission与gid关联,系统会为进程加载一些必要库,permission用于将permission与gid关联,assign-permission将system中描述的permission与uid关联等等;其中解析permission调用了readPermission()方法进行权限的解析
readPermissions()解析指定目录下的所有xml文件,比如将标签<library>
所指的动态库保存到PKMS的成员变量mSharedLibraries。可见,SystemConfig创建过程是对以下这四个目录中的所有xml进行解析
5.启动PackageHandler,创建data目录,初始化UserManagerService
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
...
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper()); // ---见5.1 建立PackageHandler的消息循环,用于处理apk的安装请求
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
File dataDir = Environment.getDataDirectory();//创建data的文件夹
//创建5个目录
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); // ---见5.2
ArrayMap<String, SystemConfig.PermissionEntry> permConfig = systemConfig.getPermissions(); // ---见5.3
...(for循环)
ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();// ---见5.3
...(for循环)
...
}
}
...
}
5.1 PackageHandler
PackageHandler是PMS的内部类,用于接收并处理外部apk安装请求信息,如adb install、packageinstaller安装apk时会发送消息。
在PackageHandler的handleMessage方法中调用doHandleMessage方法处理消息,代码如下:
class PackageHandler extends Handler {
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY:
case MCS_BOUND:
后续的消息处理都是在该函数中。
5.2 UserManagerService
在UserManagerService的构造函数中,创建data/system/users、data/system/users/0目录和data/system/users/userlist.xml文件,然后调用readUserListLocked()方法解析userlist.xml.该文件保存着用户的id.很短.
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<users nextSerialNumber="10" version="5">
<guestRestrictions>
<restrictions no_config_wifi="true" no_outgoing_calls="true" no_sms="true" />
</guestRestrictions>
<user id="0" />
</users>
5.3 操作数据结构
for循环从/system/etc/permission里面读取到的permission的name和对应的gid放入到bp中,然后保存在mSettings的mPermissions中.
for循环从/system/etc/permission中读取到的shared library 放到PMS的变量mSharedLibraries中去。
6.解析package文件
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
...
mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),mSdkVersion, mOnlyCore);//恢复上一次安装信息,下方有该方法流程图。Android系统每次启动时,都会重新安装一遍系统中的应用(5个目录,用scanDirLI安装),但有些信息每次安装都应该是一致的,比如UID,因此需要将信息保存下来。
String customResolverActivity = Resources.getSystem().getString(R.string.config_customResolverActivity);
if (TextUtils.isEmpty(customResolverActivity)) {
customResolverActivity = null;
} else {
mCustomResolverComponentName = ComponentName.unflattenFromString(
customResolverActivity);
}
...
}
}
...
}
调用Settings的readLPw函数去解析packages.xml和packages-backup.xml保存的安装列表信息,并把解析的pakcages信息添加到相应的数据结构中。Android设备第一次开机时候,所有packages.xml和packages-backup.xml文件都还不存在,会直接返回。
readLPw流程图如下所示:
7.Dexopt优化判定
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
...
//7.1
long startTime = SystemClock.uptimeMillis(); // 获取当前时间
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, startTime);
// Set flag to monitor and not change apk file paths when scanning install directories.
final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;//设置扫描模式
final ArraySet<String> alreadyDexOpted = new ArraySet<String>();// 存储已经优化的文件
/**
* Add everything in the in the boot class path to the
* list of process files because dexopt will have been run
* if necessary during zygote startup.
*/
final String bootClassPath = System.getenv("BOOTCLASSPATH");//获取BOOTCLASSPATH环境变量的值
final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");//获取SYSTEMSERVERCLASSPATH环境变量的值
if (bootClassPath != null) {
String[] bootClassPathElements = splitString(bootClassPath, ':');
for (String element : bootClassPathElements) {
alreadyDexOpted.add(element); //将BOOTCLASSPATH中的指明的文件加入已经优化的文件列表中
}
} else {
Slog.w(TAG, "No BOOTCLASSPATH found!");
}
if (systemServerClassPath != null) {
String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
for (String element : systemServerClassPathElements) {
alreadyDexOpted.add(element);//将SYSTEMSERVERCLASSPATH中的文件加入已经优化的文件列表中
}
} else {
Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
}
//7.2
final List<String> allInstructionSets = InstructionSets.getAllInstructionSets();
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(// 将allInstructionSets转化为数组
allInstructionSets.toArray(new String[allInstructionSets.size()]));
/**
* Ensure all external libraries have had dexopt run on them.
*/
if (mSharedLibraries.size() > 0) {// 当知道了系统abi的架构后,会进行对sharedLibraries的处理,这边的mSharedLibraries,是jar
// NOTE: For now, we're compiling these system "shared libraries"
// (and framework jars) into all available architectures. It's possible
// to compile them only when we come across an app that uses them (there's
// already logic for that in scanPackageLI) but that adds some complexity.
for (String dexCodeInstructionSet : dexCodeInstructionSets) {// 遍历所支持的平台,一般有arm,x86,arm64等平台
for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {
final String lib = libEntry.path;
if (lib == null) {
continue;
}
try {
int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
alreadyDexOpted.add(lib);
mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
} catch (IOException e) {
Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
+ e.getMessage());
}
}
}
}//end if
//7.3
File frameworkDir = new File(Environment.getRootDirectory(), "framework");//"/system/framework"
// Gross hack for now: we know this file doesn't contain any
// code, so don't dexopt it to avoid the resulting log spew.
alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
// Gross hack for now: we know this file is only part of
// the boot class path for art, so don't dexopt it to
// avoid the resulting log spew.
alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
/**
* There are a number of commands implemented in Java, which
* we currently need to do the dexopt on so that they can be
* run from a non-root shell.
*/
String[] frameworkFiles = frameworkDir.list();//对system/framework目录下文件进行dex优化
if (frameworkFiles != null) {
// TODO: We could compile these only for the most preferred ABI. We should
// first double check that the dex files for these commands are not referenced
// by other system apps.
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (int i=0; i<frameworkFiles.length; i++) {
File libPath = new File(frameworkDir, frameworkFiles[i]);
String path = libPath.getPath();
// Skip the file if we already did it.
if (alreadyDexOpted.contains(path)) {
continue;
}
// Skip the file if it is not a type we want to dexopt.
if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
continue;//跳过那些非apk和jar的文件
}
try {
int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);//优化后的文件放在/data/dalvik-cache
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Jar not found: " + path);
} catch (IOException e) {
Slog.w(TAG, "Exception reading jar: " + path, e);
}
}
}
}//end if
...
}
}
...
}
分为三小部分
7.1
/*BOOTCLASSPATH是Android Linux的一个环境变量,可以在adb shell下用$BOOTCLASSPATH看到。BOOTCLASSPATH即系统核心JAR包的路径,直接加入alreadyDexOpted这个HashSet,表示不需要进行dexopt操作,因为它们在zygote启动时已经进过Dex优化了
7.2
Dexopt流程如图所示:
这里先获取当前Android设备的abi列表,也就是armeabi,armeabi-v7a,arm64-v8a等等信息。
然后在每种abi情况下,利用DexFile.getDexOptNeeded检查该library是否已经执行过dexopt了。
NO_DEXOPT_NEEDED :if the apk/jar is already up to date.
DEX2OAT_NEEDED: if dex2oat should be called on the apk/jar file.
PATCHOAT_NEEDED:if patchoat should be called on the apk/jar file to patch the odex file along side the apk/jar.
SELF_PATCHOAT_NEEDED if selfpatchoat should be called on the apk/jar file to patch the oat file in the dalvik cache.
当结果不为NO_DEXOPT_NEEDED表明,该library需要dexopt.通过mInstaller的dexopt进行dexopt操作。
7.3
将 /system/framework/framework-res.apk 和 /system/framework/core-libart.jar 这两个文件加入已优化列表alreadyDexOpted中去。
然后搜索/system/framework中那些还没有dexopt的jar或者apk文件,调用dexopt流程进行优化。
7.4 升级系统时的处理
如果是升级系统时,进行的处理,假设没有进行系统升级,则忽略这段代码。
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
...
final VersionInfo ver = mSettings.getInternalVersion();
mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
// when upgrading from pre-M, promote system app permissions from install to runtime
mPromoteSystemApps =
mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
// save off the names of pre-existing system packages prior to scanning; we don't
// want to automatically grant runtime permissions for new system apps
if (mPromoteSystemApps) {
Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
while (pkgSettingIter.hasNext()) {
PackageSetting ps = pkgSettingIter.next();
if (isSystemApp(ps)) {
mExistingSystemPackages.add(ps.name);
}
}
}
...
}
}
...
}
8.调用scanDirLI方法扫描并安装APK包[重要]
在代码中通过注释来分析。
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
...
// Collect vendor overlay packages.
// (Do this before scanning any apps.)
// For security and version matching reason, only consider
// overlay packages if they reside in VENDOR_OVERLAY_DIR.
File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR); // /vendor/overlay
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);// 扫描该系统目录下的所有apk,进行安装
// 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);// /system/framework
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app"); // /system/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");// /system/app
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app"); // /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");// /oem/app
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
...
}
}
...
}
详见 扫描应用目录8.1 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;// 在传入的dir中遍历文件,如果没有文件则直接返回
}
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
for (File file : files) {// 如果有文件存在的话,就会进行遍历
// 判断一个文件是否为 .apk文件/文件夹并且文件夹满足isStageName的条件
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
try {
// 会调用scanPackageLI进行接下来的解析
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) {
// 只有非系统的apk扫描失败的时候,才会删除该apk。
logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
if (file.isDirectory()) {
mInstaller.rmPackageDir(file.getAbsolutePath());
} else {
file.delete();
}
}
}
}
}
8.1.1 isStageName方法
public static boolean isStageName(String name) {
final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
final boolean isLegacyContainer = name.startsWith("smdl2tmp");
return isFile || isContainer || isLegacyContainer;
}
8.1.2 scanPackageLI方法
//调用scanPackageLI函数扫描一个特定的文件,返回值是PackageParser的内部类package。
//该类的实例代表一个apk文件,所以它就是和apk文件对应的数据结构
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;//mDefParseFlags的值为0,parseFlags的值会随着scanFile的变化而变化
PackageParser pp = new PackageParser();//new一个文件解析器PackageParser对象
pp.setSeparateProcesses(mSeparateProcesses);//null,因为前面初始化值的时候separateProcesses为null
pp.setOnlyCoreApps(mOnlyCore);//正常情况下mOnlyCore为flase
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);
}
PackageSetting ps = null;
PackageSetting updatedPkg;
// reader
synchronized (mPackages) {
// Look to see if we already know about this package.
String oldName = mSettings.mRenamedPackages.get(pkg.packageName);
if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
// This package has been renamed to its original name. Let's
// use that.
ps = mSettings.peekPackageLPr(oldName);
}
// If there was no original package, see one for the real package name.
if (ps == null) {
/*这里主要是处理应用升级后包名不一致的情况,当设备第一次开机时,不存在这样的情况。
其他情况下,开机会解析packages.xml,当前后有apk的包名发生变化时,该app在packages.xml中会以标签标记。
而且还会把这些包名更改了的信息计入PMS的mSettings变量内的ArrayMap类型的变量mRenamedPackages中,key是newname.*/
ps = mSettings.peekPackageLPr(pkg.packageName);
}
// Check to see if this package could be hiding/updating a system
// package. Must look for it either under the original or real
// package name depending on our state.
updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);
}
boolean updatedPkgBetter = false;
// First check if this is a system package that may involve an update
if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
// If new package is not located in "/system/priv-app" (e.g. due to an OTA),
// it needs to drop FLAG_PRIVILEGED.
if (locationIsPrivileged(scanFile)) {
updatedPkg.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
} else {
updatedPkg.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
if (ps != null && !ps.codePath.equals(scanFile)) {
// The path has changed from what was last scanned... check the
// version of the new path against what we have stored to determine
// what to do.
if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath);
if (pkg.mVersionCode <= ps.versionCode) {
// The system package has been updated and the code path does not match
// Ignore entry. Skip it.
if (DEBUG_INSTALL) Slog.i(TAG, "Package " + ps.name + " at " + scanFile
+ " ignored: updated version " + ps.versionCode
+ " better than this " + pkg.mVersionCode);
if (!updatedPkg.codePath.equals(scanFile)) {
Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg : "
+ ps.name + " changing from " + updatedPkg.codePathString
+ " to " + scanFile);
updatedPkg.codePath = scanFile;
updatedPkg.codePathString = scanFile.toString();
updatedPkg.resourcePath = scanFile;
updatedPkg.resourcePathString = scanFile.toString();
}
updatedPkg.pkg = pkg;
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
"Package " + ps.name + " at " + scanFile
+ " ignored: updated version " + ps.versionCode
+ " better than this " + pkg.mVersionCode);
} else {
// The current app on the system partition is better than
// what we have updated to on the data partition; switch
// back to the system partition version.
// At this point, its safely assumed that package installation for
// apps in system partition will go through. If not there won't be a working
// version of the app
// writer
synchronized (mPackages) {
// Just remove the loaded entries from package lists.
mPackages.remove(ps.name);
}
logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile
+ " reverting from " + ps.codePathString
+ ": new version " + pkg.mVersionCode
+ " better than installed " + ps.versionCode);
InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
synchronized (mPackages) {
mSettings.enableSystemPackageLPw(ps.name);
}
updatedPkgBetter = true;
}
}
}
/*处理系统更新后,检查是否对系统app有影响。即是否将系统app更新为更高的新版本了。是的话,要处理。*/
if (updatedPkg != null) {
// An updated system app will not have the PARSE_IS_SYSTEM flag set
// initially
parseFlags |= PackageParser.PARSE_IS_SYSTEM;
// An updated privileged app will not have the PARSE_IS_PRIVILEGED
// flag set initially
if ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
}
}
// Verify certificates against what was last scanned
//描apk的签名
collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags);
/*
* A new system app appeared, but we already had a non-system one of the
* same name installed earlier.
*/
/*当系统更新后,可能更新包会多出一些system app出来,那么如果此时用户恰好安装了一个同包名的app.
*两者的签名还不一致,那么就删除扫描到的系统应用的信息。
*当两者签名一致时,如果扫描到的app版本更高,那么就删除安装的应用;如果扫描的app版本低,那么隐藏扫描到的系统应用。*/
boolean shouldHideSystemApp = false;
if (updatedPkg == null && ps != null
&& (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {
/*
* Check to make sure the signatures match first. If they don't,
* wipe the installed application and its data.
*/
if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
!= PackageManager.SIGNATURE_MATCH) {
logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but"
+ " signatures don't match existing userdata copy; removing");
deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false);
ps = null;
} else {
/*
* If the newly-added system app is an older version than the
* already installed version, hide it. It will be scanned later
* and re-added like an update.
*/
if (pkg.mVersionCode <= ps.versionCode) {
shouldHideSystemApp = true;
logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + scanFile
+ " but new version " + pkg.mVersionCode + " better than installed "
+ ps.versionCode + "; hiding system");
} else {
/*
* The newly found system app is a newer version that the
* one previously installed. Simply remove the
* already-installed application and replace it with our own
* while keeping the application data.
*/
logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile
+ " reverting from " + ps.codePathString + ": new version "
+ pkg.mVersionCode + " better than installed " + ps.versionCode);
InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
}
}
}
// The apk is forward locked (not public) if its code and resources
// are kept in different files. (except for app in either system or
// vendor path).
// TODO grab this value from PackageSettings
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
}
}
// TODO: extend to support forward-locked splits
//处理应用的代码路径和资源路径
String resourcePath = null;
String baseResourcePath = null;
if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {
if (ps != null && ps.resourcePathString != null) {
resourcePath = ps.resourcePathString;
baseResourcePath = ps.resourcePathString;
} else {
// Should not happen at all. Just log an error.
Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
}
} else {
resourcePath = pkg.codePath;
baseResourcePath = pkg.baseCodePath;
}
// Set application objects path explicitly.
pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
pkg.applicationInfo.setCodePath(pkg.codePath);
pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
pkg.applicationInfo.setResourcePath(resourcePath);
pkg.applicationInfo.setBaseResourcePath(baseResourcePath);
pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
// Note that we invoke the following method only if we are about to unpack an application
//调用PackageParser的另一个scanPackageLI继续处理, 8.1.3展开
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
| 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.
*/
/*如果扫描的系统app需要被隐藏,那么通过mSettings.disableSystemPackageLPw方法将其信息记录在mSettings的mDisabledSysPackages中。*/
if (shouldHideSystemApp) {
synchronized (mPackages) {
mSettings.disableSystemPackageLPw(pkg.packageName);
}
}
return scannedPkg;
}
8.1.3 scanPackageLI尾部调用的另一个同名方法
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
boolean success = false;
try {
final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
currentTime, user);
success = true;
return res;
} finally {
if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
removeDataDirsLI(pkg.volumeUuid, pkg.packageName);
}
}
}
该方法内部调用 scanPackageDirtyLI方法,其内部调用的createDataDirsLI方法中调用mInstaller.install进行安装,例如mActivities.addActivity(..) 把四大组件信息注册到PMS内部.
具体内容在安装篇再分析.
9.扫描用户安装的app
先处理有升级包的系统应用,也就是执行过OTA升级后,第一次启动时,需要关心的逻辑。再扫描用户安装的app
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
...
// Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
if (!mOnlyCore) {
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
//If this is not a system app, it can't be a disable system app.
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;//忽略普通应用
}
//If the package is scanned, it's not erased.
final PackageParser.Package scannedPkg = mPackages.get(ps.name);
if (scannedPkg != null) {
/*
* If the system app is both scanned and in the disabled packages list, then it must have been added via OTA.
* Remove it from the currently scanned package so the previously user-installed application can be scanned.
*/
// packages.xml中<updated-package>修饰的package会被记录到mSettings中的disable列表中去
// 这说明扫描的系统app是带有升级包的
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
logCriticalInfo(Log.WARN, "Expecting better updated system app for "
+ ps.name + "; removing system app. Last known codePath="
+ ps.codePathString + ", installStatus=" + ps.installStatus
+ ", versionCode=" + ps.versionCode + "; scanned versionCode="
+ scannedPkg.mVersionCode);
removePackageLI(ps, true);//将其从mPackages中移除
mExpectingBetter.put(ps.name, ps.codePath);// 将其添加到mExpectingBetter,后续处理
}
continue;
}
// 运行到这里说明packages.xml中记录的app,这次没有扫描到。
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
//如果这个app在packages.xml也不属于<updated-package>
//意味着这个应用是残留在packages.xml中的,可能还会剩下沙箱数据,因此也要删掉
psit.remove();
logCriticalInfo(Log.WARN, "System package " + ps.name
+ " no longer exists; wiping its data");
removeDataDirsLI(null, ps.name);
} else {
// 如果这个app在packages.xml属于<updated-package>
// 将其加入possiblyDeletedUpdatedSystemApps
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
possiblyDeletedUpdatedSystemApps.add(ps.name);
}
}
}//end while
}//end if
//扫描并删除未成功安装的apk包(针对第三方app)
//look for any incomplete package installations
ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
//clean up list
for(int i = 0; i < deletePkgsList.size(); i++) {
//clean up here
cleanupInstallFailedPackage(deletePkgsList.get(i));
}
// 删除临时文件
//delete tmp files
deleteTempPackageFiles();
// 把从mSettings中没有关联任何应用的SharedUserSetting对象删掉
mSettings.pruneSharedUsersLPw();
if (!mOnlyCore) {
//调用scanDirLI扫描用户安装的app,目录是/data/app和/data/app-priv
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);
/**
* 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.
*/
//处理possiblyDeletedUpdatedSystemApps。它里面存储的是在packages.xml中被标记为,但是前面又没有扫描到的其apk文件的app。
for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
//在扫描了用户app目录之后,再次尝试查找是否有这些app
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
String msg;
if (deletedPkg == null) {//依旧没有,那么就删除他们的数据目录
msg = "Updated system package " + deletedAppName
+ " no longer exists; wiping its data";
removeDataDirsLI(null, deletedAppName);
} else {//找到了,说明是在用户app目录中找到的,那么移除系统权限
msg = "Updated system app + " + deletedAppName
+ " no longer present; removing system privileges for "
+ deletedAppName;
deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
}
logCriticalInfo(Log.WARN, msg);
}
/**
* Make sure all system apps that we expected to appear on
* the userdata partition actually showed up. If they never
* appeared, crawl back and revive the system version.
*/
// 处理前文存放到mExpectingBetter的那些带有升级包的系统应用
for (int i = 0; i < mExpectingBetter.size(); i++) {
final String packageName = mExpectingBetter.keyAt(i);
if (!mPackages.containsKey(packageName)) {
final File scanFile = mExpectingBetter.valueAt(i);
logCriticalInfo(Log.WARN, "Expected better " + packageName
+ " but never showed up; reverting to system");
final int reparseFlags;
//确保是在/system/priv-app、system/app、vendor/app、oem/app这四个目录中。
if (FileUtils.contains(privilegedAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED;
} else if (FileUtils.contains(systemAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR;
} else if (FileUtils.contains(vendorAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR;
} else if (FileUtils.contains(oemAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR;
} else {
Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
continue;
}
//会将其加入mSettings的mPackages中
mSettings.enableSystemPackageLPw(packageName);
//重新扫描这些文件
try {
scanPackageLI(scanFile, reparseFlags, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse original system package: "
+ e.getMessage());
}
}
}
}
// 清除mExpectingBetter
mExpectingBetter.clear();
...
}
}
...
}
10.调用mSettings.writeLPr更新package.xml等文件
接下来的代码作用是更新所有应用的动态库路径,如果是OTA升级导致前后SDK版本不一致,还要进行权限重新检查,并且删除app oat cache目录。更新数据库版本,调用mSettings.writeLPr更新package.xml、package.list、runtime-permission.xml等文件。
最后创建mInstallerService对象:
mInstallerService = new PackageInstallerService(context, this);
11.总结
PMS构造方法的执行过程就是先读取保存在packages.xml中记录的系统关机前记录所有安装的app信息,保存在mSettings中的mPackages中。
然后扫描指定的若干目录中的app,并把信息记录在PMS的mPackages中。最后对两者进行对比,看是否能发现有升级的app,然后进行相关处理,最后在写入packages.xml中。