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中。

扫描应用目录

原创粉丝点击