Android Framework 包管理子系统(01)PackageManagerService启动分析

112 篇文章 91 订阅

该系列文章总纲链接:专题分纲目录 Android Framework 包管理子系统


本章关键点总结 & 说明:

导图是不断迭代的,这里主要关注➕ PkgMS启动分析部分即可。在对PkgMS有一个基础了节的基础上,分析了SystemServer启动流程和构造器(对PkgMS来讲,这是核心部分)两部分。

PackageManagerService(后面简称PkgMS)主要负责系统中Package的管理,应用程序的安装、卸载、信息查询等。


特殊说明:

  1. 这里 ApplicationPackageManager类继承自PackageManager类。它并没有直接参与Binder通信,而是通过mPM成员变量指向一个IPackageManager.Stub.Proxy类型的对象。
  2. 源码中是没有IPackageManager.java文件的。该文件是经aidl工具处理IPackageManager.aidl后得到的,输出在Android源码/out/target/。。。目录下

在Andorid5.0种,PkgMS有两个重要的变量mInstallService(属于PackageInstallerService类型,用于管理应用程序安装)和mInstaller(属于InstallerConnection类型,和Daemon进程installd通过socket进行通信,实际上apk的格式转换、建立数据目录等工作都是由InstallD来完成),都与应用安装有着密切的关系。关系如下图所示:

PkgMS由SystemServer创建,在SystemServer中有关PkgMS的启动,分成几个部分
@1 在SystemServer中startBootstrapServices中相关代码如下:

//SS->startBootstrapServices
private void startBootstrapServices() {
	Installer installer = mSystemServiceManager.startService(Installer.class);
	//...
	PackageManagerService m = new PackageManagerService(context, installer,...);
	ServiceManager.addService("package", m);//注册PkgMS到ServiceManager中
	//...
	mPackageManagerService = PackageManagerService.main(mSystemContext, installer,...);
	mFirstBoot = mPackageManagerService.isFirstBoot();//这里mFirstBoot会传递到WMS中
	mPackageManager = mSystemContext.getPackageManager();
	//...
}

@2 在SystemServer中startCoreServices中相关代码如下:

//SS->startCoreServices
private void startCoreServices() {
	// Update after UsageStatsService is available, needed before performBootDexOpt.
	mPackageManagerService.getUsageStatsIfNoPackageUsageInfo();
}

@3 在SystemServer中startOtherServices中相关代码如下:

//SS->startOtherServices
private void startOtherServices() {
    //...
	mPackageManagerService.performBootDexOpt();//dex优化
	//...
	mPackageManagerService.systemReady();//通知系统进入就绪状态
    //...
}

 这里首先分析PkgMS的main函数,实现如下:

public static final PackageManagerService main(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {
    //调用PkgMS的构造函数,factoryTest和onlyCore的值均为false
    PackageManagerService m = new PackageManagerService(context, installer,
            factoryTest, onlyCore);
    //向ServiceManager注册PKMS
    ServiceManager.addService("package", m);
    return m;
}

这里创建了pkgMS,之后向ServiceManager注册,但执行时间很长,因为在pkgMS构造器中做了很多事情,因此这里主要以分析pkgMS的构造方法为主,分析思路如下:

  1. 扫描目标文件夹的前期工作
  2. 扫描目标文件夹
  3. 扫描后期工作

接下来我们一步步分析。由于代码段比较长,这里通过分段分析的方式,集中分析关键点。

1 扫描目标文件夹的前期工作

分析构造函数第一阶段,代码如下:

public PackageManagerService(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
//第一阶段,start
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
            SystemClock.uptimeMillis());
    //mSdkVersion取自系统属性“ro.build.version.sdk” 即编译的SDK版本。
    if (mSdkVersion <= 0) {
        Slog.w(TAG, "**** ro.build.version.sdk not set!");
    }

    mContext = context;
    mFactoryTest = factoryTest; //false,运行在非工厂模式下
    mOnlyCore = onlyCore; //为false,运行在普通模式下
    mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
    mMetrics = new DisplayMetrics();//显示相关属性
	
    mSettings = new Settings(context);
    //关键点1:setting相关,分析addSharedUserLPw函数
    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);
//第一阶段,end

1.1 setting解读

@1 addSharedUserLPw

分析addSharedUserLPw函数,调用代码如下:

mSettings.addSharedUserLPw(
"android.uid.system", //字符串
Process.SYSTEM_UID,//系统进程使用的用户id,值为1000
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED//标识系统Package
);

ApplicationInfo.FLAG_PRIVILEGED标志说明:对应privileged app,那么哪些app属于privileged app(特权app)?

特权app首先是System app,然后要具有ApplicationInfo.PRIVATE_FLAG_PRIVILEGED标志。 有两类app属于privileged app:特定uid的app 和 /system/framework/system/priv-app目录下的app

以上对addSharedUserLPw的几个参数进行说明,接下来分析addSharedUserLPw实现,代码如下:

SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {
    SharedUserSetting s = mSharedUsers.get(name);
    if (s != null) {
        if (s.userId == uid) {
            return s;
        }
        return null;
    }
    //创建SharedUserSettings对象,并设置的userId为uid,
    s = new SharedUserSetting(name, pkgFlags);
    s.userId = uid;
    if (addUserIdLPw(uid, s, name)) {
        //mSharedUsers是一个HashMap:key为字符串,value为SharedUserSetting对象
        mSharedUsers.put(name, s);
        return s;
    }
    return null;
}

这里mSharedUsers是Setting中的成员变量,存储 字符串与SharedUserSetting键值对,也就是说以字符串为key得到对应的SharedUserSetting对象。下面对SharedUserSetting进行解读。

@2 SharedUserSetting分析

这里以 AndroidManifest.xml 为例,配置文件如下:

<manifestxmlns:...
       android:sharedUserId="android.uid.system"
       android:process="system">

在xml中,声明了一个名为android:sharedUserId的属性,其值为“android.uid.system”。sharedUserId和UID有关,有两个作用:

  1. 两个以上声明了同一种sharedUserIds的APK可共享彼此的数据,可运行在同一进程
  2. 通过声明特定的sharedUserId,该APK所在进程将被赋予指定的UID(将该进程的uid设置为system的uid)

特殊说明:除在AndroidManifest.xml中声明sharedUserId外,APK还需要在编译时还必须使用对应的证书进行签名。如果在Android.mk中,则需要额外声明LOCAL_CERTIFICATE := platform,如此,才可获得指定的UID。

如何组织一种数据结构来包括上面的内容?(关注下面3个关键点)

  1. XML 中 sharedUserId属性指定了一个字符串,是UID相关的字符串描述,故对应数据结构中也应该有这样一个字符串(源码和XML中属性联系起来了)。
  2. Linux系统中UID是一个整数,所以该数据结构中必然有一个整型变量。
  3. 多个Package可声明同一个sharedUserId,因此该数据结构必然会保存那些声明了相同sharedUserId的Package的信息。

接下来看Android是如何设计相应数据结构的,类图如下:

类图说明:

  1. Settings类定义了一个mSharedUsers成员,HashMap类型,以字符串(如“android.uid.system”)为Key,对应Value是一个SharedUserSettings对象。
  2. SharedUserSetting与权限相关,其成员变量packages,HashSet类型,保存声明了相同sharedUserId的Package权限设置的信息。每个Package有自己的权限设置。权限由PackageSetting类表达。

1.2 XML文件扫描

这里继续分析构造函数第二阶段,代码如下:

//第二阶段,start
    // TODO: add a property to control this?
    long dexOptLRUThresholdInMinutes;
    if (mLazyDexOpt) {
        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;

    //该值和调试有关,一般不设置
    String separateProcesses = SystemProperties.get("debug.separate_processes");
    if (separateProcesses != null && separateProcesses.length() > 0) {
        if ("*".equals(separateProcesses)) {
            mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
            mSeparateProcesses = null;
        } else {
            mDefParseFlags = 0;
            mSeparateProcesses = separateProcesses.split(",");
        }
    } else {
        mDefParseFlags = 0;
        mSeparateProcesses = null;
    }

    mInstaller = installer; //初始化Native进程installd交互
    apk的安装和卸载最终都是调用installd来实现
    getDefaultDisplayMetrics(context, mMetrics);//获取当前设备的显示屏信息
    
    //创建SystemConfig,这里会调用readPermissions各种权限
    SystemConfig systemConfig = SystemConfig.getInstance();
    mGlobalGids = systemConfig.getGlobalGids();
    mSystemPermissions = systemConfig.getSystemPermissions();
    mAvailableFeatures = systemConfig.getAvailableFeatures();

    //环境变量初始化
    synchronized (mInstallLock) {
    // writer
    synchronized (mPackages) {
        mHandlerThread = new ServiceThread(TAG,
                Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
        //创建一个ThreadHandler对象,该线程的工作是:程序的安装和卸载
        mHandlerThread.start();
        mHandler = new PackageHandler(mHandlerThread.getLooper());
        Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);

        File dataDir = Environment.getDataDirectory();
        // mAppDataDir指向/data/data目录
        mAppDataDir = new File(dataDir, "data");
        mAppInstallDir = new File(dataDir, "app");
        mAppLib32InstallDir = new File(dataDir, "app-lib");
        mAsecInternalPath = new File(dataDir, "app-asec").getPath();
        // mUserAppDataDir指向/data/user目录
        mUserAppDataDir = new File(dataDir, "user");
        // mDrmAppPrivateInstallDir指向/data/app-private目录
        mDrmAppPrivateInstallDir = new File(dataDir, "app-private");

        sUserManager = new UserManagerService(context, this,
                mInstallLock, mPackages);

        //权限注册到 package manager,一个权限与几个组ID对应,当一个APK授予这个权限时,它同属于这几个组
        ArrayMap<String, SystemConfig.PermissionEntry> permConfig
                = systemConfig.getPermissions();
        //遍历权限配置文件
        for (int i=0; i<permConfig.size(); i++) {
            SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
            //拿到权限
            BasePermission bp = mSettings.mPermissions.get(perm.name);
            if (bp == null) {
                bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
                //写入权限
                mSettings.mPermissions.put(perm.name, bp);
            }
            if (perm.gids != null) {
                bp.gids = appendInts(bp.gids, perm.gids);
            }
        }

        ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
        for (int i=0; i<libConfig.size(); i++) {
            mSharedLibraries.put(libConfig.keyAt(i),
                    new SharedLibraryEntry(libConfig.valueAt(i), null));
        }

        mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
        mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
                mSdkVersion, mOnlyCore);

        String customResolverActivity = Resources.getSystem().getString(
                R.string.config_customResolverActivity);
        if (TextUtils.isEmpty(customResolverActivity)) {
            customResolverActivity = null;
        } else {
            mCustomResolverComponentName = ComponentName.unflattenFromString(
                    customResolverActivity);
        }
        long startTime = SystemClock.uptimeMillis();
//第二阶段,end

@1 这里关注➕ SystemConfig类的创建与构造,这里使用了getInstance,即单例模式,对应代码如下:

public static SystemConfig getInstance() {
    synchronized (SystemConfig.class) {
        if (sInstance == null) {
            sInstance = new SystemConfig();
        }
        return sInstance;
    }
}

继续分析,构造器实现代码如下:

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);
}

继续分析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()) {
        //先处理该目录下的非platform.xml文件
        if (f.getPath().endsWith("etc/permissions/platform.xml")) {
            platformFile = f;
            continue;
        }

        if (!f.getPath().endsWith(".xml")) {
            Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
            continue;
        }

        if (!f.canRead()) {
            Slog.w(TAG, "Permissions library file " + f + " cannot be read");
            continue;
        }
        //调用readPermissionFromXml解析XML后缀的文件
        readPermissionsFromXml(f, onlyFeatures);
    }

    // Read platform permissions last so it will take precedence
    //单独处理 /system/etc/permissions/platform.xml文件。
    if (platformFile != null) {
        readPermissionsFromXml(platformFile, onlyFeatures);
    }
}

这里关键是调用readPermissionFromXml函数解析/system/etc/permissions,/system/etc/sysconfig目录下的文件以及vendor/etc/permissions,/vendor/etc/sysconfig目录下的文件,而这些文件都是在编译阶段由不同硬件平台根据自己的配置信息复制相关文件到目标目录中得来的,这里继续分析readPermissionsFromXml,代码如下:

private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
    FileReader permReader = null;
    try {
        permReader = new FileReader(permFile);
    } catch (FileNotFoundException e) {
        Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
        return;
    }

    final boolean lowRam = ActivityManager.isLowRamDeviceStatic();

    try {
        XmlPullParser parser = Xml.newPullParser();
        parser.setInput(permReader);

        //XML解析相关...
    } finally {
        IoUtils.closeQuietly(permReader);
    }
    //...
}

这里主要是将XML中的标签转换成对应的数据结构,这些数据结构的目的是为了保存XML中各种标签及它们之间的关系。在分析和使用中,重要的是理解各标签的作用。

@2 mSettings.readLPw函数

readLPw的功能也是解析文件,只不过这些文件在PkgMS启动后生成,部分关注➕代码如下:

boolean readLPw(PackageManagerService service, List<UserInfo> users, int sdkVersion,
        boolean onlyCore) {
    FileInputStream str = null;
    if (mBackupSettingsFilename.exists()) {
        try {
            str = new FileInputStream(mBackupSettingsFilename);
            mReadMessages.append("Reading from backup settings file\n");
            PackageManagerService.reportSettingsProblem(Log.INFO,
                    "Need to read from backup settings file");
            if (mSettingsFilename.exists()) {
                mSettingsFilename.delete();
            }
        } catch (java.io.IOException e) {
            // We'll try for the normal settings file.
        }
    }
	//...
    if (mBackupStoppedPackagesFilename.exists()
            || mStoppedPackagesFilename.exists()) {
        // Read old file
        readStoppedLPw();
        mBackupStoppedPackagesFilename.delete();
        mStoppedPackagesFilename.delete();
        // Migrate to new file format
        writePackageRestrictionsLPr(0);
    } else {
        if (users == null) {
            readPackageRestrictionsLPr(0);
        } else {
            for (UserInfo user : users) {
                readPackageRestrictionsLPr(user.id);
            }
        }
    }
	//...
    return true;
}

这里关注两组文件,说明如下:

  1. packages.xml和packages-backup.xml为一组,用于描述系统中所安装的Package的信息,其中backup是临时文件。PKMS先把数据写到backup中,信息都写成功后再改名成非backup的文件。其目的是防止在写文件过程中出错,导致信息丢失。
  2. packages-stopped.xml和packages-stopped-backup.xml为一组,用于描述系统中强制停止运行的pakcage的信息,backup也是临时文件。如果此处存在该临时文件,表明此前系统因为某种原因中断了正常流程。

readLPw的函数功能就是解析其中XML文件内容,建立并更新对应的数据结构,例如停止的package重启之后依然是stopped状态。同时,这里简单介绍一下这些文件(忽略backup文件):

  1. packages.xml:PkgMS扫描完目标文件夹后创建。当系统进行程序安装、卸载和更新时会更新该文件。它保存了系统中与package相关的一些信息。
  2. packages.list:描述系统中存在的所有非系统自带的APK的信息。当这些APK程序有变动时,PkgMS更新该文件。
  3. packages-stopped.xml:从Setting程序中进入应用程序页面,然后在选择强制停止(ForceStop)某个应用时,系统会将该应用的相关信息记录到此文件中。该文件保存系统中被用户强制停止的Package的信息。

1.3 总结

对以上部分进行简单总结:PkgMS扫描并解析XML文件,将其中的信息保存到特定的数据结构中。
扫描的XML文件、权限、上一次扫描得到的Package信息有关,它为PkgMS下一阶段的工作提供了重要的参考信息。

2 扫描目标文件夹

扫描系统中的APK时,由于需要逐个扫描文件,因此手机上安装程序越多,PkgMS工作量越大,系统启动速度也就越慢。

2.1 dex优化

//第三阶段,start
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                startTime);
        //定义扫描参数
        final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING;

        final ArraySet<String> alreadyDexOpted = new ArraySet<String>();

        final String bootClassPath = System.getenv("BOOTCLASSPATH");
        final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");

        if (bootClassPath != null) {
            String[] bootClassPathElements = splitString(bootClassPath, ':');
            for (String element : bootClassPathElements) {
                alreadyDexOpted.add(element);
            }
        }

        if (systemServerClassPath != null) {
            String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
            for (String element : systemServerClassPathElements) {
                alreadyDexOpted.add(element);
            }
        }

        final List<String> allInstructionSets = getAllInstructionSets();
        final String[] dexCodeInstructionSets =
            getDexCodeInstructionSets(allInstructionSets.toArray(new String[allInstructionSets.size()]));

        //Ensure all external libraries have had dexopt run on them.
        if (mSharedLibraries.size() > 0) {
            for (String dexCodeInstructionSet : dexCodeInstructionSets) {
                for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {
                    final String lib = libEntry.path;
                    if (lib == null) {
                        continue;
                    }

                    try {
                        //判断该jar包是否需要重新做dex优化
                        byte dexoptRequired = DexFile.isDexOptNeededInternal(lib, null,
                                                                             dexCodeInstructionSet,
                                                                             false);
                        if (dexoptRequired != DexFile.UP_TO_DATE) {
                            alreadyDexOpted.add(lib);

                            // The list of "shared libraries" we have at this point is
                            if (dexoptRequired == DexFile.DEXOPT_NEEDED) {
                                //将该jar包文件路径保存到libFiles中,然后通过mInstall对象发送,命令给installd,让其对该jar包进行dex优化
                                mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet);
                            } else {
                                //如果是art模式,调用Install的patchoat转换成oat文件
                                mInstaller.patchoat(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet);
                            }
                        }
                    } catch (FileNotFoundException e) {
                        //...
                    }
                }
            }
        }

        File frameworkDir = new File(Environment.getRootDirectory(), "framework");
        alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
        alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");

        //对framework下的文件执行dex到odex的转换
        String[] frameworkFiles = frameworkDir.list();
        if (frameworkFiles != null) {
            for (String dexCodeInstructionSet : dexCodeInstructionSets) {
                for (int i=0; i<frameworkFiles.length; i++) {
                    //判断该目录下的apk或jar文件是否需要做dex优化。
                    File libPath = new File(frameworkDir, frameworkFiles[i]);
                    String path = libPath.getPath();
                    //忽略已经在alreadyDexOpted列表的文件
                    if (alreadyDexOpted.contains(path)) {
                        continue;
                    }
                    //忽略apk和jar意外的文件
                    if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
                        continue;
                    }
                    try {
                        //判断该jar包是否需要重新做dex优化
                        byte dexoptRequired = DexFile.isDexOptNeededInternal(path, null,
                                                                             dexCodeInstructionSet,
                                                                             false);
                        if (dexoptRequired == DexFile.DEXOPT_NEEDED) {
                            //转换成dex格式
                            mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet);
                        } else if (dexoptRequired == DexFile.PATCHOAT_NEEDED) {
                            mInstaller.patchoat(path, Process.SYSTEM_UID, true, dexCodeInstructionSet);
                        }
                    } catch (FileNotFoundException e) {
                        //...
                    }
                }
            }
        }
//第三阶段,end

这段代码主要是将必要的apk和jar文件进行优化(优化通过installer来做)

2.2 扫描系统pacakge

//第四阶段,start
        //扫描vendor/overlay目录,搜集目录中文件的信息
        File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
        scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
                | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);

        //在第三个参数中设置了SCAN_NO_DEX标志,因为该目录下的package在前面的流程中已经过判断并根据需要做过dex优化了
        //扫描system/framework目录,搜集目录中文件的信息
        scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
                | PackageParser.PARSE_IS_SYSTEM_DIR
                | PackageParser.PARSE_IS_PRIVILEGED,
                scanFlags | SCAN_NO_DEX, 0);

        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);

        ///扫描system/app目录,搜集目录中文件的信息
        final File systemAppDir = new File(Environment.getRootDirectory(), "app");
        scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
                | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

        //扫描vendor/app目录,搜集目录中文件的信息
        File vendorAppDir = new File("/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);

        //扫描oem/app目录,搜集目录中文件的信息
        final File oemAppDir = new File(Environment.getOemDirectory(), "app");
        scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
                | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
        //调用installd执行/system/etc/updatecmds下命令脚本
        mInstaller.moveFiles();
//第四阶段,end

这里将扫描以下几个目录(这里只挑关键几个文件夹进行说明):

  1. /system/frameworks:该目录文件都是系统库,例如framework.jar、services.jar、framework-res.apk。因为这里scanDirLI只扫描APK文件,这里只有framework-res.apk。
  2. /system/app:该目录下存放系统级应用,例如Phone.apk、Contacts.apk等。
  3. /system/priv-app:与/system/app功能类似,只不过拥有更多的权限,例如Setting,SystemUI等。
  4. /vendor/app:该目录文件由厂商提供,即厂商特定的APK文件。

因为主要调用scanDirLI函数对应用目录进行扫描,因此对该函数进行分析,代码如下:

private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
    final File[] files = dir.listFiles();//列举该目录下的文件
	//...

    for (File file : files) {
        final boolean isPackage = (isApkFile(file) || file.isDirectory())
                && !PackageInstallerService.isStageName(file.getName());
        if (!isPackage) {//这里只扫描APK 文件
            // Ignore entries which are not packages
            continue;
        }
        try {
            scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                    scanFlags, currentTime, null);
        } catch (PackageManagerException e) {
            //... 删除安装失败的文件和目录
            }
        }
    }
}

这里继续分析scanPackageLI,代码实现如下:

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;//默认的扫描标志,正常情况下为0
    //创建一个PackageParser对象
    PackageParser pp = new PackageParser();
    pp.setSeparateProcesses(mSeparateProcesses);
    pp.setOnlyCoreApps(mOnlyCore);
    pp.setDisplayMetrics(mMetrics);

    if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
        parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
    }

    final PackageParser.Package pkg;
    try {
        //解析参数重传递的scanFile文件。
        pkg = pp.parsePackage(scanFile, parseFlags);
    } catch (PackageParserException e) {
        throw PackageManagerException.from(e);
    }

    //...处理更改了包名的应用
    //... 处理安装升级包的系统应用
	
    //扫描文件签名
    collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags);
    //...
	
    if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
        if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
            parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
        }
    }

    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 {
        //...
    }

    //设置应用相关路径
    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);

    //注意这里调用了另一个scanPackageLI函数,参数不同
    PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
            | SCAN_UPDATE_SIGNATURE, currentTime, user);

    if (shouldHideSystemApp) {
        synchronized (mPackages) {
            //如果扫描的应用带有升级包,把它们的关系保存到mSettings中
            grantPermissionsLPw(pkg, true, pkg.packageName);
            mSettings.disableSystemPackageLPw(pkg.packageName);
        }
    }
    return scannedPkg;
}

PackageParser的目的是完成从物理文件到对应数据结构的转换,在scanPackageLI的返回值 (即PackageParser的内部类Package类的实例)代表一个APK文件,它就是和APK文件对应的数据结构。

@1 解析APK文件(PackageParser函数)

这里首先对PackageParser进行解析,它主要负责解析APK文件中的AndroidManifest.xml,PackageParser.parsePackage方法代码实现如下:

public Package parsePackage(File packageFile, int flags) throws PackageParserException {
    if (packageFile.isDirectory()) {
        return parseClusterPackage(packageFile, flags);
    } else {
        return parseMonolithicPackage(packageFile, flags);
    }
}

这里继续分析2个关键函数,parseClusterPackage和parseMonolithicPackage。由于这里面的调用关系并不简单,因此这里仅给出调用栈,整个函数执行的目的只有一个,那就是解析Androidmanifest,两个方法的调用栈分别如下:

@@1.1 parseClusterPackage解析,调用栈如下:

parseClusterPackage(File packageDir, int flags) 
    parseClusterPackageLite(packageDir, 0);
        循环处理:parseApkLite(file, flags);
           openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
           parseApkLite(apkPath, res, parser, attrs, flags, signatures);//xml处理
    parseBaseApk(baseApk, assets, flags);
        openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
        parseBaseApk(Resources res, XmlResourceParser parser, int flags,String[] outError) //Parse the manifest of a <em>base APK</em>.
    循环处理:parseSplitApk(pkg, i, assets, flags);

@@1.2 parseMonolithicPackage解析

parseMonolithicPackage(File apkFile, int flags)
    parseMonolithicPackageLite(apkFile, flags);
        parseApkLite(packageFile, flags);//xml处理
            openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
            parseApkLite(apkPath, res, parser, attrs, flags, signatures);//xml处理
    parseBaseApk(baseApk, assets, flags);
        openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
        parseBaseApk(Resources res, XmlResourceParser parser, int flags,String[] outError) //Parse the manifest of a <em>base APK</em>.
    循环处理:parseSplitApk(pkg, i, assets, flags);

说明:以上分析忽略AssetManager、Resource等相关代码。主要是调用parseApkLite 和 parsebaseApk 来解析AndroidMinfest。

PackageParser定了相当多的内部类,这些内部类的作用就是保存对应的信息。解析AndroidManifest.xml文件得到的信息由Package保存。从该类的成员变量可看出,和Android四大组件相关的信息分别由activites、receivers、providers、services保存。以PackageParser.Activity为例,它从Component<ActivityIntentInfo>派生。Component是一个模板类,元素类型是ActivityIntentInfo,此类的顶层基类是IntentFilter。PackageParser.Activity内部有一个ActivityInfo类型的成员变量,该变量保存的就是四大组件中Activity的信息。那么为什么不直接使用ActivityInfo,而是通过IntentFilter构造出一个使用模板的复杂类型PackageParser.Activity呢?

因为Package除了保存信息外,还需要支持Intent匹配查询。

说明:PackageParser定了一个轻量级的数据结构PackageLite,该类仅存储Package的一些简单信息。我们在介绍Package安装的时候,会遇到PackageLite。

@2 scanPackageLI函数(再次被调用)

在PackageParser扫描完一个APK后,系统根据该APK中AndroidManifest.xml创建一个完整的Package对象,下一步就是将该Package加入到系统中。此时调用的函数就是另外一个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.packageName);
        }
    }
}

这里主要关注scanPackageDirtyLI()函数(过长,因此这里仅作功能描述),功能如下:

  1. 建立ResolverActivity内存对象:当发出intent时,如果系统中存在多个可以响应该intent的Activity,android系统会弹出一个框让给用户选择,这个框就是ResolverActivity,保存在PkgMS的mResolverActivity变量中,相关代码位于framework-res.apk中
  2. 升级相关:处理<original-package>标签,且有升级包,则将包的信息加入到mSettings.mRenamedPackages变量中,这些信息保存到package.xml中的<renamed-package>标签下。
  3. 校验和整理工作:校验签名;检查ContentProvider名称;调用fixprocessName来确定将来进程名称;创建应用数据目录,有错误直接删除
  4. 安装系统库文件:应用中带了动态库,安装在/data/data/<package-name>/lib下,如果是系统应用,则展开动态库到/system/lib下
  5. 重新优化dex文件:动态库发生变化则重新生成odex文件。
  6. 提取应用中组件信息:把应用中的Activity信息、Service信息、Provider信息、Receiver信息、Permission信息。。。等都提取出来,加入到PkgMS的成员变量中,比如:
  7.     // All available activities, for your resolving pleasure.
        final ActivityIntentResolver mActivities = new ActivityIntentResolver();
    
        // All available receivers, for your resolving pleasure.
        final ActivityIntentResolver mReceivers = new ActivityIntentResolver();
    
        // All available services, for your resolving pleasure.
        final ServiceIntentResolver mServices = new ServiceIntentResolver();
    
        // All available providers, for your resolving pleasure.
        final ProviderIntentResolver mProviders = new ProviderIntentResolver();

     

2.3 扫描非系统Package

非系统Package就是指那些不存储在系统目录下的APK文件,该部分处理代码如下:

//第五阶段,start
        //这里记录的是可能有升级包的系统应用
        final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
        final ArrayMap<String, File> expectingBetter = new ArrayMap<>();
        if (!mOnlyCore) {mOnlyCore用于控制是否扫描非系统Package
		    //循环处理mSettings.mPackages的应用信息
            Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
            while (psit.hasNext()) {
                //...
            }
        }

        //扫描并删除未安装成功的apk包
        ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
        //clean up list
        for(int i = 0; i < deletePkgsList.size(); i++) {
            //删除安装不成功的文件及临时文件
            cleanupInstallFailedPackage(deletePkgsList.get(i));
        }
        deleteTempPackageFiles();
        mSettings.pruneSharedUsersLPw();

		//处理非系统应用
        if (!mOnlyCore) {
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                    SystemClock.uptimeMillis());
            // 扫描/data/app,收集文件目录中的信息
            scanDirLI(mAppInstallDir, 0, scanFlags, 0);
			// 扫描/data/app_private,收集文件目录中的信息
            scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
                    scanFlags, 0);
			//...
            for (int i = 0; i < expectingBetter.size(); i++) {
                final String packageName = expectingBetter.keyAt(i);
                if (!mPackages.containsKey(packageName)) {
                    final File scanFile = expectingBetter.valueAt(i);
                    final int reparseFlags;
					//确保应用位于下面的4个系统应用目录
                    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 {
                        continue;
                    }
                    mSettings.enableSystemPackageLPw(packageName);
					//扫描并处理应用
                    scanPackageLI(scanFile, reparseFlags, scanFlags, 0, null);
                }
            }
        }
		//更新所有应用的动态库路径
        updateAllSharedLibrariesLPw();

        for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
            adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
                    false /* force dexopt */, false /* defer dexopt */);
        }
        mPackageUsage.readLP();

        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                SystemClock.uptimeMillis());
//第五阶段,end

2.4 总结

目录说明:

  1. 系统Package目录包括:/system/frameworks、/system/app和/vendor/app。
  2. 非系统Package目录包括:/data/app、/data/app-private。

PkgMS构造函数第三阶段 要创建比较多的对象,所以它是一个耗时耗内存的操作。

3 扫描后期工作

该部分主要是将第二部分收集的信息再集中整理一次,比如将有些信息保存到文件中,如下所示:

//第六阶段,start
        final boolean regrantPermissions = mSettings.mInternalSdkPlatform
                != mSdkVersion;
        mSettings.mInternalSdkPlatform = mSdkVersion;
         //汇总并更新和Permission相关的信息
        updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
                | (regrantPermissions
                        ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
                        : 0));
        if (!mRestoredSettings && !onlyCore) {
            mSettings.readDefaultPreferredAppsLPw(this, 0);
        }

        // OTA后第一次启动,需要清除cache
        mIsUpgrade = !Build.FINGERPRINT.equals(mSettings.mFingerprint);
        if (mIsUpgrade && !onlyCore) {
            for (String pkgName : mSettings.mPackages.keySet()) {
                deleteCodeCacheDirsLI(pkgName);
            }
            mSettings.mFingerprint = Build.FINGERPRINT;
        }
        //将信息写到package.xml、package.list及package-stopped.xml文件中
        mSettings.updateInternalDatabaseVersion();
        mSettings.writeLPr();
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                SystemClock.uptimeMillis());


        mRequiredVerifierPackage = getRequiredVerifierLPr();
    } // synchronized (mPackages)
    } // synchronized (mInstallLock)
    //创建PackageInstallerService
    mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
    Runtime.getRuntime().gc();//启动内存垃圾回收
}
//第六阶段,end

至此,整个PkgMS的 构造函数就分析结束了。代码很长,在这里使用分段分析的方式,忽略的地方很少,构造器的源码基本完整还原。

 

 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值