Android6.0 PackageManagerService(PMS)-构造函数
来源:互联网 发布:网络言论自由 高中作文 编辑:程序博客网 时间:2024/06/09 15:49
目录如上所示~~↑↑↑
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。eng只对30分钟内所使用的APP做优化。(??)long dexOptLRUThresholdInMinutes;if (mLazyDexOpt) {//如果为eng,则mLazyDexOpt为truedexOptLRUThresholdInMinutes = 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;mMetrics = new DisplayMetrics();//new一个DisplayMetrics对象,用于保存屏幕像素参数,匹配APK中的asset和resourceString separateProcesses = SystemProperties.get("debug.separate_processes");//debug.separate_processes:用于标记是否在独立进程中运行某个程序。//根据该值设置PMS.mDefParseFlags和PMS.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的共享UIDif (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.1mSharedUsers.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下面所声明的所有的gidmSystemPermissions = 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 systemreadPermissions(Environment.buildPath(Environment.getRootDirectory(), "etc", "sysconfig"), false);// Read configuration from the old permissions dirreadPermissions(Environment.buildPath(Environment.getRootDirectory(), "etc", "permissions"), false);// Only read features from OEM configreadPermissions(Environment.buildPath(Environment.getOemDirectory(), "etc", "sysconfig"), true);readPermissions(Environment.buildPath(Environment.getOemDirectory(), "etc", "permissions"), true);}其中rootDirectory是“/system”.oemDirectory是”/oem”.也就是会尝试依次读取
/system/etc/sysconfig /system/etc/permissions /oem/etc/sysconfig /oem/etc/permissions
这四个目录中的permission文件。
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 filesFile 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 precedenceif (platformFile != null) {//最后单独处理platform.xml文件readPermissionsFromXml(platformFile, onlyFeatures);}}
readPermissions方法的作用就是读取指定目录下的xml文件,然后调用readPermissionsFromXml方法来解析xml文件中的permission内容,保存在不同的数据结构中。
详见 处理permission文件
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的内部类,用于接收并处理其他模块程序发送的消息,这些消息可以来自adb或者其他应用程序。
在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
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) {// writersynchronized (mPackages) {...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); }...}}...}调用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) {// writersynchronized (mPackages) {...//7.1long 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.2final 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.3File 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();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); } } 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) {// writersynchronized (mPackages) {...final VersionInfo ver = mSettings.getInternalVersion();mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);// when upgrading from pre-M, promote system app permissions from install to runtimemPromoteSystemApps =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 appsif (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) {// writersynchronized (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/overlayscanDirLI(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-appscanDirLI(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/appscanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);// Collect all vendor packages.File vendorAppDir = new File("/vendor/app"); // /vendor/apptry {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/appscanDirLI(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) {// writersynchronized (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>// 将其加入possiblyDeletedUpdatedSystemAppsfinal 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 installationsArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();//clean up listfor(int i = 0; i < deletePkgsList.size(); i++) {//clean up herecleanupInstallFailedPackage(deletePkgsList.get(i));}// 删除临时文件//delete tmp filesdeleteTempPackageFiles();// 把从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()); } } } }// 清除mExpectingBettermExpectingBetter.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中。
扫描应用目录
- Android6.0 PackageManagerService(PMS)-构造函数
- Android6.0 PackageManagerService(PMS)-简介
- Android6.0 PackageManagerService(PMS)-安装
- Android6.0 PackageManagerService(PMS)-卸载
- pms包管理服务分析-PackageManagerService构造函数和包扫描过程
- Android6.0 PackageManagerService卸载应用
- Android6.0 PackageManagerService 安装lib
- Android6.0 PackageManagerService dex优化
- Android7.0 PackageManagerService (2) PKMS构造函数的主要工作
- PackageManagerService.java 构造函数的 分析
- PackageManagerService学习笔记六-构造函数
- Android之PackageManagerService构造函数部分分析
- Android PackageManagerService分析一:PMS的启动
- Android PackageManagerService分析一:PMS的启动
- PackageManagerService(Android5.1)深入分析(一)构造函数
- PackageManagerService对象构造过程
- (OK) Android PackageManagerService分析一:PMS的启动
- pms
- I
- 十五张思维导图带你快速学习PHP基础知识
- 落单的数
- C++虚基类
- STM32F简单应用
- Android6.0 PackageManagerService(PMS)-构造函数
- 策略路由 概述
- Maven-pom文件内置属性
- opencv下使用摄像头
- bootstrap-table显示数据时显示No matching records found
- Shell script 的默认变数(`$0, $1`...)等
- PSI and index do not match: PSI and index do not match
- 浅谈pdfobject.js实现网页PDF文件浏览
- 【剑指offer】面试题 43 : 1~n 整数中 1 出现的次数