#############################################

本文为极度寒冰原创,转载请注明出处 #############################################

1 PackageManager

/*常用命令*/
adb shell dumpsys package (dump出系统中所有的application信息)
adb shell dumpsys package “com.android.contacts" p (dump出系统中特定包名的application信息)
/*常用命令*/
一: PackageManager服务
在Android系统中,和用户关系最密切的service应该是PackageManager了。
一般来说,用户想要在Android Phone上进行自己感兴趣的活动,都少不了apk的支持。
不论是打电话,上网,发短信还是玩一些自己喜欢的游戏,这些内容在android的世界里都是以apk的形式存在的。
所以,apk的安装,卸载是与每个用户息息相关的。
我们接下来会用一些文章去解析PackageManager的工作原理,apk的安装和卸载的过程。
在SystemServer中,经过前面的分析,我们知道了启动Android的系统关键服务的函数首先就是startBootstrapServices.
private void startBootstrapServices() {    // Wait for installd to finish starting up so that it has a chance to    // create critical directories such as /data/user with the appropriate    // permissions.  We need this to complete before we initialize other services.    mInstaller = mSystemServiceManager.startService(Installer.class); // Install是pm路径下面的一个单独的类,主要用于通过InstallerConnection建立和installd的链接                                                                      // 然后Installd会进行创建一些系统关键目录的作用,所以我们要等待Installd的结束,才可以继续进行其它的创建.....    // Only run "core" apps if we're encrypting the device.    String cryptState = SystemProperties.get("vold.decrypt");    if (ENCRYPTING_STATE.equals(cryptState)) {        Slog.w(TAG, "Detected encryption in progress - only parsing core apps");        mOnlyCore = true;    } else if (ENCRYPTED_STATE.equals(cryptState)) {        Slog.w(TAG, "Device encrypted - only parsing core apps");        mOnlyCore = true;    }    // Start the package manager.    Slog.i(TAG, "Package Manager");    // mOnlyCore 用于判断是否只扫描系统的目录    mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,            mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore); // mFactoryTestMode 用于判断是否为工厂测试,正常情况下mOnlyCore = false    mFirstBoot = mPackageManagerService.isFirstBoot();    mPackageManager = mSystemContext.getPackageManager();.....}
在经过systemServer的启动后,我们知道了系统将会通过PackageManagerService的main函数进入到PackageManager的实际工作中。
PackageManagerService的main函数其实很简单,只有一点点的内容。
public static final PackageManagerService main(Context context, Installer installer,        boolean factoryTest, boolean onlyCore) {    PackageManagerService m = new PackageManagerService(context, installer,            factoryTest, onlyCore);    ServiceManager.addService("package", m);    return m;}
我们知道了PackageManager的main函数其实主要的实现是通过PackageManagerService的构造函数实现的。
那么构造函数的实现也是比较多的,做了很多耗时的工作。
其中,PackageManager的构造函数的主要功能为:
1. 扫描Android系统中几个目标文件夹中的apk,并且建立相应的数据结构去管理Package的信息,四大组件的信息,权限信息等内容
2. 解析物理的apk的文件,生成符合自己需求的数据结构。
所以,学习PackageManager最重要的就是学习保存各种信息的数据结构和他们之间的关系,以及控制的策略
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

二: PackageManager的构造函数之前期准备
/*UID和GID*/
如果你是一个Linux的用户,那么一定会有如下的体验。当你的Linux的系统里面,存在多个用户的时候,你在登录了用户A之后,是没有权限去修改用户B的文件内容的。
再举个极端的例子,用普通用户的权限是没有办法进行Root用户所特有的操作的。
UID: Linux系统用于区别不同的用户,使用不同的用户名是一种方法。但是用户名只是一种让人方便读的字符串,对机器来讲是没有意义的。
为了方便机器的读取,Linux采用了一个32位的整数记录和区分不同的用户。这个用来区分不同用户的数字被称为User ID,简称UID。
Root的UID为0,普通的UID一般是从500开始。Android系统不同,后续会进行分析
GID: 除了用户的概念,Linux的系统还有用户组的概念。同一个用户组的用户之间具有相似的特征。
假如我们把某一个用户加入到root组,那么这个用户就可以浏览root用户组目录的文件。 如果root用户把某个文件的读写执行权限开放,root用户组的所有用户都可以修改该文件。
/*PackageManager 构造函数解析*/
   public PackageManagerService(Context context, Installer installer,      boolean factoryTest, boolean onlyCore) {    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,        SystemClock.uptimeMillis());       if (mSdkVersion <= 0) { // 在最开始,我们会去检查sdk的version,因为如果检查不到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")); //判断build的类型,一般为eng,user版本,eng的版本一般是不会进行odex的优化的        mMetrics = new DisplayMetrics(); // 初始化一个DisplayMetrics,用于保存屏幕像素的参数        mSettings = new Settings(context); // new了一个Settings的对象,这个settings是pm里面的,主要是保存系统apk的相关设置,互相之间关系等内容  --1        mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);  // --2        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);        String separateProcesses = SystemProperties.get("debug.separate_processes");        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 = " + mDefParseFlags + " mSeparateProcesses " + mSeparateProcesses);        }        mInstaller = installer;        getDefaultDisplayMetrics(context, mMetrics);        SystemConfig systemConfig = SystemConfig.getInstance();        mGlobalGids = systemConfig.getGlobalGids();        mSystemPermissions = systemConfig.getSystemPermissions();        mAvailableFeatures = systemConfig.getAvailableFeatures();......}

/* --1 Settings的分析*/
/*
Settings的构造函数如下,在构造函数中主要是创建了/data/system的目录,并且创建了packages.xml, packages-backup.xml, packages.list, packages-stopped.xml等文件。
这些文件又都有一些具体的作用:
packages.xml: 是保存了系统所有的Package信息
packages-backup.xml: 是packages.xml的备份,防止在写packages.xml突然断电
packages.list: 保存了系统中已经安装的apk,以及对应的data/data/下面的对应关系
packages-stopped.xml: 用于记录系统中强制停止运行的Package信息
packages-stopped-backup.xml: 是packages-stopped.xml的备份,防止在写packages-stopped-backup的时候突然断电
*/
    Settings(Context context) {        this(context, Environment.getDataDirectory());    }    Settings(Context context, File dataDir) {        mSystemDir = new File(dataDir, "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");        mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");        mPackageListFilename = new File(mSystemDir, "packages.list");        FileUtils.setPermissions(mPackageListFilename, 0660, SYSTEM_UID, PACKAGE_INFO_GID);        // Deprecated: Needed for migration        mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");        mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");    }
/*在初始化了Settings的文件之后,就进行了addSharedUserLPw的操作*/
/*
mSharedUsers是一个hashmap,HashMap<String, SharedUserSetting>,保存的是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;
*/
    SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {        SharedUserSetting s = mSharedUsers.get(name); // 从当前的mSharedUsers里面寻找对应名字的字符串是否已经有了对应        if (s != null) {  // 如果已经存在的话,就返回对应的UID            if (s.userId == uid) {                return s;            }            // 如果从hashmap里面找到了这个String name,但是两个的uid却不一样的话,那么就会报错。因为一个shared user不可能有两个UID,只会保留第一个            PackageManagerService.reportSettingsProblem(Log.ERROR,                    "Adding duplicate shared user, keeping first: " + name);            return null;        }        s = new SharedUserSetting(name, pkgFlags); // 如果从mSharedUsers里面没有找到这个name的话,就会去新建一个        s.userId = uid; // 赋值UID        if (addUserIdLPw(uid, s, name)) { // 将index和sharedUserSettings保存在mUserIds和mOtherUserIds            mSharedUsers.put(name, s); // 讲name和sharedUserSettings保存在mSharedUsers            return s;        }        return null;    }
/*接下来就会进入到addUserIdLPw的函数中*/
/*
UID前面讲了,是Linux的一种安全机制。
在android系统里面,继承并扩展了这种安全机制。
Android的系统,不同的应用程序之间原则上是不可以互相访问数据的。除非运行在同一个进程,或者通过contentProvider或其他的机制。
这个安全机制的主要原因,也就是android为每一个app都分配了一个UID,每个app从Linux的层面上面,都是一个独立的用户。
所以可以解释了这个安全机制的实现原理。
*/
    private boolean addUserIdLPw(int uid, Object obj, Object name) {        if (uid > Process.LAST_APPLICATION_UID) { // 系统为app分配的UID为10000 - 19999,如果超出了这个范围就会报错            return false;        }        /*           mUserIds是一个ArrayList,也就是一个动态的数组           private final ArrayList<Object> mUserIds = new ArrayList<Object>();           举个例子,如果是uid=10001的用户,说明是一个app,那么这个时候就会进入到app的相应处理中去           会去读去mUserIds的size.           在第一次读取的时候,N肯定是为0的。但是index肯定不会为0,因为在apk安装的时候就会为其分配一个UID           所以在第一次读取的时候,index一定是>=N的。           这个时候,会add null,其实就是什么也没有做,而只是对N进行++           所以,N的数字,一定是比前面遍历过得apk的uid的index大1的           N只是用来统计当前的ArrayList里面的apk,index的数目           mOtherUserIds的定义如下:是一个SparseArray。可以通过put,append两种方式写入数据           private final SparseArray<Object> mOtherUserIds = new SparseArray<Object>();        */        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++;            }            /*如果通过index从mUserIds里面读取到了数据,那肯定是错误的。因为一个app只有一个index,且只能被赋值一次*/            if (mUserIds.get(index) != null) {                PackageManagerService.reportSettingsProblem(Log.ERROR,                        "Adding duplicate user id: " + uid                        + " name=" + name);                return false;            }            mUserIds.set(index, obj); // 将当前的obj设置到ArrayList对应的index中        } 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;    }
/*
mUserIds是一个ArrayList,也就是一个动态的数组
private final ArrayList<Object> mUserIds = new ArrayList<Object>();

举个例子,如果是uid=10001的用户,说明是一个app,那么这个时候就会进入到app的相应处理中去
会去读去mUserIds的size.

在第一次读取的时候,N肯定是为0的。但是index肯定不会为0,因为在apk安装的时候就会为其分配一个UID
所以在第一次读取的时候,index一定是>=N的。
这个时候,会add null,其实就是什么也没有做,而只是对N进行++
所以,N的数字,一定是比前面遍历过得apk的uid的index大1的
N只是用来统计当前的ArrayList里面的apk,index的数目

mOtherUserIds的定义如下:是一个SparseArray。可以通过put,append两种方式写入数据
private final SparseArray<Object> mOtherUserIds = new SparseArray<Object>();
*/

/*
通过上述的分析,我们可以知道在PackageManager的构造函数中,先将系统的一些关键进程的UID进行了添加
为什么这样做呢?举个例子

在Settings里面,AndroidManifest里面有如下的声明:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settings"
coreApp="true"
android:sharedUserId="android.uid.system">

关注的焦点为sharedUserId为android.uid.system,设置这个可以使生成的apk能够获取system权限。
可以在任意system权限目录下面进行目录或者文件的创建,以及访问其他apk资源等

进行这个设置,也应该是让后面的app的uid在进行添加的时候,可以找到对应的android.uid.system的权限

然后在实际添加的过程中,调用了addSharedUserLPw函数,在这个函数中,我们会去检查是否当前的UID已经在mSharedUsers中存在,如果存在,就返回已经存在的对象。如果UID不一致,则会报错。
如果不存在的话,就会去new一个SharedUserSetting的对象。

在SharedUserSetting中,主要存在以下的对象。
final String name; --> name
int userId; --> 对应的UID
int uidFlags; --> 对应的FLAG
final HashSet<PackageSetting> packages = new HashSet<PackageSetting>(); --> 一个PackageSetting的对象,
final PackageSignatures signatures = new PackageSignatures(); --> 签名

在new出来SharedUserSetting的对象后,会将传递进来的UID进行赋值给userId,这样的话,结合构造函数,就完成了对
name, userId, uidFlags的赋值

然后执行addUserIdLPw函数,会将当前的SharedUserSetting对象和index进行关联,如果是app的话存入到了mUserIds的动态数组,如果是系统的userId的话,将对应的SharedUserSetting存入到了mOtherUserIds的hashmap中
*/

/*
在构造函数中,下面的code部分也是非常重要的
*/
SystemConfig systemConfig = SystemConfig.getInstance(); // 单例模式,获取SystemConfig的实例
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();

/*
SystemConfig 的构造函数如下,可以看到,主要是去读取了一些系统的权限相关的信息
读取的路径是/ststem/etc/目录
*/
    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) { // 并且读取onlyFeatures的值为flase的时候                Slog.w(TAG, "No directory " + libraryDir + ", skipping");            }            return;        } // /system/etc/sysconfig 会进入到No directory skipping        if (!libraryDir.canRead()) {  // 如果文件夹不可读的话,会返回            Slog.w(TAG, "Directory " + libraryDir + " cannot be read");            return;        }        // Iterate over the files in the directory and scan .xml files        for (File f : libraryDir.listFiles()) { // 遍历目录下面的文件夹            // We'll read platform.xml last            if (f.getPath().endsWith("etc/permissions/platform.xml")) { // 最后再去处理platform.xml                continue;            }            if (!f.getPath().endsWith(".xml")) { // 如果文件的路径不是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;            }            readPermissionsFromXml(f, onlyFeatures);        }        // Read permissions from .../etc/permissions/platform.xml last so it will take precedence        final File permFile = new File(Environment.getRootDirectory(),                "etc/permissions/platform.xml");        readPermissionsFromXml(permFile, onlyFeatures);    }
然后最终会通过readPermissionsFromXml去进行文件的读取
/*
* 正如前面的文章分析init.rc时,我们知道针对这个文本的解析,都会有一个文本的解析器。
* 主要是对文本中的各个标签进行解析,并且对应成了相应的数据结构
* 解析android的system config的permission也是基于这个思想进行的解析
* 下面就来具体分析一下这个解析的过程和对应的数据结构
*/
private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {/*   FileReader fr = new FileReader(String fileName);//使用带有指定文件的String参数的构造方法。创建该输入流对象。并关联源文件。   主要方法:   int read(); // 读取单个字符。返回作为整数读取的字符,如果已达到流末尾,则返回 -1。   int read(char []cbuf);//将字符读入数组。返回读取的字符数。如果已经到达尾部,则返回-1。   void close();//关闭此流对象。释放与之关联的所有资源。   XmlPullParser:   常用的有:   XmlPullParser.END_DOCUMENT   XmlPullParser.START_DOCUMENT   XmlPullParser.START_TAG   XmlPullParser.END_TAG   XmlPullParser.TEXT   分别代表着XML文档的结束,开始,标签的开始,标签的结束,内容   XmlPullParser.getEventType() : Returns the type of the current event (START_TAG, END_TAG, TEXT, etc.) 【获取当前事件回调类型】   XmlPullParser.getName():For START_TAG or END_TAG events, the (local) name of the current element is returned when namespaces are enabled.【获取当前节点名字】   XmlPullParser.getAttributeValue(int index):Returns the given attributes value.【根据id获取节点属性值】   XmlPullParser.getAttributeValue(String namespace, String name):Returns the attributes value identified by namespace URI and namespace localName.【根据name获取节点属性值】   XmlPullParser.netxText():  If current event is START_TAG then if next element is TEXT then element content is returned or if next event is END_TAG then empty string is returned, otherwise exception is thrown.【回调节点START_TAG时,通过此方法获取节点内容】*/    FileReader permReader = null;    try {        permReader = new FileReader(permFile);    } catch (FileNotFoundException e) {        Slog.w(TAG, "Couldn't find or open permissions file " + permFile);        return;    }    try {        XmlPullParser parser = Xml.newPullParser();        parser.setInput(permReader);        int type;        /*         * parser.next() 指向下一个标签         */        while ((type=parser.next()) != parser.START_TAG                   && type != parser.END_DOCUMENT) {            ;        } // 如果下一个标签不是START_TAG的话,会一直寻找到下一个标签        if (type != parser.START_TAG) { // 如果遍历完了,都没有遍历到一个标签的开始的话,会返回error            throw new XmlPullParserException("No start tag found");        }        /*         * parser.getName() 获取标签的标题         */        if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {//如果获取到的标签的标题不为permissions,或者解析出来的标签的标题不为config的话,会报错/*    以android.hardware.nfc.hce.xml为例,该文件中的内容为:    <permissions>        <feature name="android.hardware.nfc.hce" />    </permissions>*/            throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +                    ", expected 'permissions' or 'config'");        }/*具体的标签的属性如下:group: 安装到系统中的所有 APK 都具备的组 ID。permission: 可以指定一个权限与几个组 ID 对应。当一个 APK 被授予这个权限时,它也同时属于这几个组。assign-permission: 把一个权限赋予一个 UID,当进程使用这个 UID 运行时,就具备了这个权限。library: 为系统添加一些扩展库用的。对应的.jar 文件放在/system/framework/目录下。比如Google Map 相关的库。feature: 每添加一个硬件,都要增加对应的feature*/        while (true) { // 如果是permissions和config的话,会进入到这个while循环,用来解析这个大的标签,和配置文件            XmlUtils.nextElement(parser); // 读取permissions的大标签下面的每一个小标签            if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {                break;            }            String name = parser.getName();/*    <permission name="android.permission.MODIFY_NETWORK_ACCOUNTING">        <group gid="net_bw_acct" />    </permission>*/            if ("group".equals(name) && !onlyFeatures) {                String gidStr = parser.getAttributeValue(null, "gid"); // 如果tag的name是group的话,根据gid去获取当前的属性                if (gidStr != null) {                    int gid = android.os.Process.getGidForName(gidStr);                    mGlobalGids = appendInt(mGlobalGids, gid); // 将gid保存到mGlobalGids的数组中                } else {                    Slog.w(TAG, "<group> without gid at "                            + parser.getPositionDescription());                }                XmlUtils.skipCurrentTag(parser);                continue;            } else if ("permission".equals(name) && !onlyFeatures) { // 如果tag的name是permission的话,根据name去获取当前的属性                String perm = parser.getAttributeValue(null, "name");                if (perm == null) {                    Slog.w(TAG, "<permission> without name at "                            + parser.getPositionDescription());                    XmlUtils.skipCurrentTag(parser);                    continue;                }                perm = perm.intern();  //  返回string池中的perm,此处perm的值并没有改变                readPermission(parser, perm); // 调用readPermission进行perm的解析,该函数的解析我们放到后面进行分析  ----1/*    <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />    <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />    <assign-permission name="android.permission.WAKE_LOCK" uid="media" />    <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="media" />    <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="media" />    <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />*/            } else if ("assign-permission".equals(name) && !onlyFeatures) { // 如果tag的name是assign-permission的话,会根据name, uid去获取相应的属性                String perm = parser.getAttributeValue(null, "name"); // 获取name                if (perm == null) {                    Slog.w(TAG, "<assign-permission> without name at "                            + parser.getPositionDescription());                    XmlUtils.skipCurrentTag(parser);                    continue;                }                String uidStr = parser.getAttributeValue(null, "uid"); // 获取UID                if (uidStr == null) {                    Slog.w(TAG, "<assign-permission> without uid at "                            + parser.getPositionDescription());                    XmlUtils.skipCurrentTag(parser);                    continue;                }                int uid = Process.getUidForName(uidStr);   // 通过uid的string,获取到真正的UID                if (uid < 0) {                    Slog.w(TAG, "<assign-permission> with unknown uid \""                            + uidStr + "\" at "                            + parser.getPositionDescription());                    XmlUtils.skipCurrentTag(parser);                    continue;                }                perm = perm.intern(); // permission的name                HashSet<String> perms = mSystemPermissions.get(uid);  // 通过uid去perms的hashset中查询,看是否可以查询的到。                if (perms == null) {   // 如果查询不到的话                    perms = new HashSet<String>();  // 会新建一个perms                    mSystemPermissions.put(uid, perms);  //然后把uid和对应的hashset放到mSystemPermissions的SparseArray中                }                perms.add(perm); // 将新的permission放入到perms中                XmlUtils.skipCurrentTag(parser);                // 通过这个操作,我们可以知道目前的数据结构是mSystemPermissions是一个SparseArray,然后是通过uid建立的映射。保存的是当前的UID所拥有的权限            } else if ("library".equals(name) && !onlyFeatures) {/*<library name="android.test.runner" file="/system/framework/android.test.runner.jar" /><library name="javax.obex" file="/system/framework/javax.obex.jar"/>*/                String lname = parser.getAttributeValue(null, "name");  // 通过name和file去获取对应的属性                String lfile = parser.getAttributeValue(null, "file");                if (lname == null) {                    Slog.w(TAG, "<library> without name at "                            + parser.getPositionDescription());                } else if (lfile == null) {                    Slog.w(TAG, "<library> without file at "                            + parser.getPositionDescription());                } else {                    //Log.i(TAG, "Got library " + lname + " in " + lfile);                    mSharedLibraries.put(lname, lfile);   // 如果都有值的话,会把name和file都push到mSharedLibraries中                }                XmlUtils.skipCurrentTag(parser);                continue;            } else if ("feature".equals(name)) {/*<permissions>    <feature name="android.hardware.usb.accessory" />    <library name="com.android.future.usb.accessory"            file="/system/framework/com.android.future.usb.accessory.jar" /></permissions>*/                String fname = parser.getAttributeValue(null, "name");  // 在feature的tag下,会获取feature的name,每增加一个硬件,都要增加对应的feature                if (fname == null) {                    Slog.w(TAG, "<feature> without name at "                            + parser.getPositionDescription());                } else {                    //Log.i(TAG, "Got feature " + fname);                    FeatureInfo fi = new FeatureInfo();                    fi.name = fname;                    mAvailableFeatures.put(fname, fi); // 将对应的feature name和其对应的FeatureInfo放入到了mAvailableFeatures中                }                XmlUtils.skipCurrentTag(parser);                continue;            } else if ("allow-in-power-save".equals(name)) {/*    allow-in-power-save, 应该是在省电模式下面允许该package获取网络的权限。    目前的话,只有platform.xml里面一处有这个标签的声明    <!-- These are the standard packages that are white-listed to always have internet         access while in power save mode, even if they aren't in the foreground. -->    <allow-in-power-save package="com.android.providers.downloads" />*/                String pkgname = parser.getAttributeValue(null, "package");                if (pkgname == null) {                    Slog.w(TAG, "<allow-in-power-save> without package at "                            + parser.getPositionDescription());                } else {                    mAllowInPowerSave.add(pkgname); // 将该pkgname保存到mAllowInPowerSave中                }                XmlUtils.skipCurrentTag(parser);                continue;            } else if ("fixed-ime-app".equals(name)) { // 目前的permission中,并没有fixed-ime-app这个标签                String pkgname = parser.getAttributeValue(null, "package");                if (pkgname == null) {                    Slog.w(TAG, "<fixed-ime-app> without package at "                            + parser.getPositionDescription());                } else {                    mFixedImeApps.add(pkgname);                }                XmlUtils.skipCurrentTag(parser);                continue;            } else {                XmlUtils.skipCurrentTag(parser);                continue;            }        }        permReader.close();    } catch (XmlPullParserException e) {        Slog.w(TAG, "Got execption parsing permissions.", e);    } catch (IOException e) {        Slog.w(TAG, "Got execption parsing permissions.", e);    }}
在前面的解析中,我们留下了一个函数,是在permission的标签的时候,会用readPermission去进行解析
在这个函数中,我们会把permission和对应的gid进行保存。
void readPermission(XmlPullParser parser, String name) // name是permission的name,parser是读取到的数据流        throws IOException, XmlPullParserException {    name = name.intern();    PermissionEntry perm = mPermissions.get(name); // 去mPermissions中查看是否已经有name对应的PermissionEntry    if (perm == null) {        perm = new PermissionEntry(name);        mPermissions.put(name, perm);    }  // 如果没有的话,会new一个PermissionEntry    int outerDepth = parser.getDepth();    int type;    while ((type=parser.next()) != XmlPullParser.END_DOCUMENT           && (type != XmlPullParser.END_TAG                   || parser.getDepth() > outerDepth)) {        if (type == XmlPullParser.END_TAG                || type == XmlPullParser.TEXT) {            continue;        }        String tagName = parser.getName();        if ("group".equals(tagName)) {            String gidStr = parser.getAttributeValue(null, "gid");            if (gidStr != null) {                int gid = Process.getGidForName(gidStr);                perm.gids = appendInt(perm.gids, gid);            } else {                Slog.w(TAG, "<group> without gid at "                        + parser.getPositionDescription());            }        }        XmlUtils.skipCurrentTag(parser);    }}
分析到了现在,我们分析完了systemConfig的构造函数。
在systemConfig的阶段,我们会去读取/system/etc的permission内容,然后进行解析,保存在不同的数据结构中.
我们接着回到PackageManager的构造函数中:

/*
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids(); // 系统的etc的xml下面所声明的所有的gid
mSystemPermissions = systemConfig.getSystemPermissions(); // 系统中etc下xml声明的uid和其对应的权限
mAvailableFeatures = systemConfig.getAvailableFeatures(); // 系统中硬件所拥有的feature
*/

接下来我们继续分析PackageManager的构造函数
synchronized (mInstallLock) {// writersynchronized (mPackages) {    mHandlerThread = new ServiceThread(TAG,            Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);    mHandlerThread.start();    mHandler = new PackageHandler(mHandlerThread.getLooper()); //  建立PackageHandler的消息循环,用于处理apk的安装请求    Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);    File dataDir = Environment.getDataDirectory(); // 创建data的文件夹    mAppDataDir = new File(dataDir, "data"); // /data/data    mAppInstallDir = new File(dataDir, "app"); // data/app    mAppLib32InstallDir = new File(dataDir, "app-lib"); // /data/app-lib    mAsecInternalPath = new File(dataDir, "app-asec").getPath();  // /data/app-ases    mUserAppDataDir = new File(dataDir, "user");   // /data/user    mDrmAppPrivateInstallDir = new File(dataDir, "app-private");  // /data/app-private    sUserManager = new UserManagerService(context, this,            mInstallLock, mPackages);....    }}
在进行完data的文件夹创建之后,会进入下面的一些操作:
    ArrayMap<String, SystemConfig.PermissionEntry> permConfig            = systemConfig.getPermissions(); // return 的是SystemConfig的mPermissions,存放的是permission的name和gid    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);        }    }    // 该for循环的作用是将前面从/system/etc/permission里面读取到的permission的name和对应的gid放入到bp中,然后保存在mSettings的mPermissions中    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));    }    // 该for循环的作用是将读取出来的SharedLibrary放入到mSharedLibraries中    mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
/*
这里首先调用Settings的readLPw函数去解析packages.xml和packages-backup.xml保存的安装列表信息
并把解析的pakcages信息添加到相应的数据结构中
这里我们先假设这是第一次开机,所有packages.xml和packages-backup.xml文件都还不存在。
所以Settings的readLPw函数会直接返回。

packages.xml里面保存了当前系统中保存的所有apk的详细信息
*/
    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(); // 获取当前系统的时间    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; // 设置扫描模式    final HashSet<String> alreadyDexOpted = new HashSet<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");    final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");    if(Debug_chao) {/*    bootClassPath为    /system/framework/core-libart.jar:    /system/framework/conscrypt.jar:    /system/framework/okhttp.jar:    /system/framework/core-junit.jar:    /system/framework/bouncycastle.jar:    /system/framework/ext.jar:    /system/framework/framework.jar:    /system/framework/telephony-common.jar:    /system/framework/voip-common.jar:    /system/framework/ims-common.jar:    /system/framework/mms-common.jar:    /system/framework/android.policy.jar:    /system/framework/apache-xml.jar*/        Log.e(TAG,"bootClassPath = " + bootClassPath);/*    /system/framework/services.jar:    /system/framework/ethernet-service.jar:    /system/framework/wifi-service.jar*/        Log.e(TAG,"systemServerClassPath = " + systemServerClassPath);    }    if (bootClassPath != null) { // 将bootClassPath的值,加入到alreadyDexOpted的hashset中/*    BOOTCLASSPATH的赋值为:    PRODUCT_BOOTCLASSPATH := $(subst $(space),:,$(foreach m,$(DEXPREOPT_BOOT_JARS_MODULES),/system/framework/$(m).jar))    DEXPREOPT_BOOT_JARS_MODULES := $(PRODUCT_BOOT_JARS)    PRODUCT_BOOT_JARS := \        core-libart \        conscrypt \        okhttp \        core-junit \        bouncycastle \        ext \        framework \        telephony-common \        voip-common \        ims-common \        mms-common \        android.policy \        apache-xml \*/        String[] bootClassPathElements = splitString(bootClassPath, ':');        for (String element : bootClassPathElements) {            alreadyDexOpted.add(element);        }    } else {        Slog.w(TAG, "No BOOTCLASSPATH found!");    }/*    # The order of PRODUCT_SYSTEM_SERVER_JARS matters.    PRODUCT_SYSTEM_SERVER_JARS := \        services \        ethernet-service \        wifi-service*/    if (systemServerClassPath != null) {        String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');        for (String element : systemServerClassPathElements) {            alreadyDexOpted.add(element);        }    } else {        Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");    }    boolean didDexOptLibraryOrTool = false;    final List<String> allInstructionSets = getAllInstructionSets(); ---1    final String[] dexCodeInstructionSets = // 将allInstructionSets转化为数组        getDexCodeInstructionSets(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; // 获取lib的path                if (lib == null) {                    continue;                }                try {    /*     * 该函数为L上面新增的接口     *     * Returns UP_TO_DATE if the VM believes that the apk/jar file     * is up to date, PATCHOAT_NEEDED if it believes that the file is up     * to date but it must be relocated to match the base address offset,     * and DEXOPT_NEEDED if it believes that it is out of date and should     * be passed through "dexopt" again.     *     * @param fileName the absolute path to the apk/jar file to examine.     * @return DEXOPT_NEEDED if dexopt should be called on the file,     *         PATCHOAT_NEEDED if we need to run "patchoat" on it and     *         UP_TO_DATE otherwise.     */                    byte dexoptRequired = DexFile.isDexOptNeededInternal(lib, null,                                                                         dexCodeInstructionSet,                                                                         false);                    if (dexoptRequired != DexFile.UP_TO_DATE) {  // 如果不是UP_TO_DATE                        alreadyDexOpted.add(lib); // 会将lib path加入到alreadyDexOpted                        // The list of "shared libraries" we have at this point is                        if (dexoptRequired == DexFile.DEXOPT_NEEDED) {  // 如果是DEXOPT_NEEDED的话,灰烬行dexopt的优化                            mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet);                        } else {  // 如果不是的话,那就是PATCHOAT_NEEDED,会进行oat的优化                            mInstaller.patchoat(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet);                        }                        didDexOptLibraryOrTool = true;  // 只要进行了oat或者opt的优化的话,会将该变量置为true                    }                } 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());                }            }        }    }
-------------------------------------------
1. getAllInstructionSets函数的实现:
    private static List<String> getAllInstructionSets() {        final String[] allAbis = Build.SUPPORTED_ABIS; // 获取当前手机所支持的硬件架构        final List<String> allInstructionSets = new ArrayList<String>(allAbis.length); // new一个List,长度即为获取出来的ABI的个数        for (String abi : allAbis) { // 从所有的支持的架构开始遍历,abi为armeabi,armeabi-v7a....            final String instructionSet = VMRuntime.getInstructionSet(abi); // 从以下的2,3可以看到,是从目前android系统支持的架构map中,去查找,查找出来一般为arm,mips,arm64...            if (!allInstructionSets.contains(instructionSet)) { // 如果查询到的不包含在我们从Build的supported里面的话,把其加入到allInstructionSets里面                allInstructionSets.add(instructionSet); // 第一次的时候,因为allInstructionSets为一个刚new出来的对象,所以肯定是会加入到allInstructionSets里面            }        }        return allInstructionSets;    }
2. VMRuntime getInstructionSet的函数的实现:
    public static String getInstructionSet(String abi) {        final String instructionSet = ABI_TO_INSTRUCTION_SET_MAP.get(abi);        if (instructionSet == null) {            throw new IllegalArgumentException("Unsupported ABI: " + abi);        }        return instructionSet;    }
3. ABI_TO_INSTRUCTION_SET_MAP的声明:
    private static final Map<String, String> ABI_TO_INSTRUCTION_SET_MAP            = new HashMap<String, String>();    static {        ABI_TO_INSTRUCTION_SET_MAP.put("armeabi", "arm");        ABI_TO_INSTRUCTION_SET_MAP.put("armeabi-v7a", "arm");        ABI_TO_INSTRUCTION_SET_MAP.put("mips", "mips");        ABI_TO_INSTRUCTION_SET_MAP.put("mips64", "mips64");        ABI_TO_INSTRUCTION_SET_MAP.put("x86", "x86");        ABI_TO_INSTRUCTION_SET_MAP.put("x86_64", "x86_64");        ABI_TO_INSTRUCTION_SET_MAP.put("arm64-v8a", "arm64");    }---------------------------------------------------------------------------------    File frameworkDir = new File(Environment.getRootDirectory(), "framework"); // /system/framework    // Gross hack for now: we know this file doesn't contain any    // code, so don't dexopt it to avoid the resulting log spew.    alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk"); // 将framework-res.apk加入到alreadyDexOpted中    // 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");    /**     * And there are a number of commands implemented in Java, which     * we currently need to do the dexopt on so that they can be     * run from a non-root shell.     */    String[] frameworkFiles = frameworkDir.list();  // 或者/system/framework下的所有文件    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(); // path是绝对路径                // Skip the file if we already did it.                if (alreadyDexOpted.contains(path)) { // 如果该文件是已经包含在alreadyDexOpted中了,就忽略不去处理                    continue;                }                // Skip the file if it is not a type we want to dexopt.                if (!path.endsWith(".apk") && !path.endsWith(".jar")) { // 如果不是以jar和apk结尾的话,就忽略。因为odex优化针对的就是jar和apk                    continue;                }                try {                    byte dexoptRequired = DexFile.isDexOptNeededInternal(path, null,                                                                         dexCodeInstructionSet,                                                                         false);  // 判断文件是否需要优化                    if (dexoptRequired == DexFile.DEXOPT_NEEDED) {  // 如果需要dex的话,就会去进行Dex的优化                        mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet);                        didDexOptLibraryOrTool = true;                    } else if (dexoptRequired == DexFile.PATCHOAT_NEEDED) { // 如果是需要oat优化的话,就会去进行oat的优化                        mInstaller.patchoat(path, Process.SYSTEM_UID, true, dexCodeInstructionSet);                        didDexOptLibraryOrTool = true;                    }                } catch (FileNotFoundException e) {                    Slog.w(TAG, "Jar not found: " + path);                } catch (IOException e) {                    Slog.w(TAG, "Exception reading jar: " + path, e);                }            }        }    }
// 在接下来的处理中,就会遇到packagemanager的一个重要的函数,scanDirLI,我们接下来继续分析。
    // (Do this before scanning any apps.)    // For security and version matching reason, only consider    // overlay packages if they reside in VENDOR_OVERLAY_DIR.    /*     * private static final String VENDOR_OVERLAY_DIR = "/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); // 扫描该系统目录下的所有apk,进行安装 -1    // 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);    // Collected privileged system packages.    final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");    scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM            | PackageParser.PARSE_IS_SYSTEM_DIR            | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);    // Collect ordinary system packages.    final File systemAppDir = new File(Environment.getRootDirectory(), "app");    scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);    // Collect all vendor packages.    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);    // Collect all OEM packages.    final File oemAppDir = new File(Environment.getOemDirectory(), "app");    scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);    if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");    mInstaller.moveFiles();   
-------------------------------------------------------------------------------------------------------------------
/*
* scanDirLI的实现如下:
*/
    private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {        final File[] files = dir.listFiles(); // 会从传入的dir中,遍历所有的文件        if (ArrayUtils.isEmpty(files)) {  // 如果该文件夹中一个文件都没有的话,会return            Log.d(TAG, "No files in app dir " + dir);            return;        }        if (DEBUG_PACKAGE_SCANNING) {            Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags                    + " flags=0x" + Integer.toHexString(parseFlags));        }        for (File file : files) {  // 如果有文件存在的话,就会进行遍历            final boolean isPackage = (isApkFile(file) || file.isDirectory())                    && !PackageInstallerService.isStageName(file.getName()); // 判断一个文件是否是一个apk的文件(以apk结尾),或者是一个文件并且文件夹满足isStageName的条件            if (!isPackage) {                // Ignore entries which are not packages                continue;            }            try {                scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,                        scanFlags, currentTime, null); // 会利用scanPackageLI进行接下来的解析            } 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()) {                        FileUtils.deleteContents(file);                    }                    file.delete();                }            }        }    }
---------------------------------------------------------------------------------------------------------------------
    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;    }
---------------------------------------------------------------------------------------------------------------------
  /*     *  Scan a package and return the newly parsed package.     *  Returns null in case of errors and the error code is stored in mLastScanError     *  调用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;        PackageParser pp = new PackageParser(); // new 了一个PackageParser的对象        pp.setSeparateProcesses(mSeparateProcesses);        pp.setOnlyCoreApps(mOnlyCore);        pp.setDisplayMetrics(mMetrics);        if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {            parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;        }        /*         * 各个参数的值如下:           mSeparateProcesses = null,因为前面初始化值的时候separateProcesses为null           mOnlyCore = false   正常情况下mOnlyCore为flase           mMetrics 为前面获取到的手机的分辨率           mDefParseFlags 的值为0           parseFlags的值会随着scanFile的变化而变化         */        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) {                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 (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.                    logCriticalInfo(Log.INFO, "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();                        // This is the point at which we know that the system-disk APK                        // for this package has moved during a reboot (e.g. due to an OTA),                        // so we need to reevaluate it for privilege policy.                        if (locationIsPrivileged(scanFile)) {                            updatedPkg.pkgFlags |= ApplicationInfo.FLAG_PRIVILEGED;                        }                    }                    updatedPkg.pkg = pkg;                    throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, null);                } 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, ps.legacyNativeLibraryPathString,                            getAppDexInstructionSets(ps));                    synchronized (mInstallLock) {                        args.cleanUpResourcesLI();                    }                    synchronized (mPackages) {                        mSettings.enableSystemPackageLPw(ps.name);                    }                    updatedPkgBetter = true;                }            }        }        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.pkgFlags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {                parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;            }        }        // Verify certificates against what was last scanned        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.         */        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, ps.legacyNativeLibraryPathString,                            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.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.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.         */        if (shouldHideSystemApp) {            synchronized (mPackages) {                /*                 * We have to grant systems permissions before we hide, because                 * grantPermissions will assume the package update is trying to                 * expand its permissions.                 */                grantPermissionsLPw(pkg, true, pkg.packageName);                mSettings.disableSystemPackageLPw(pkg.packageName);            }        }        return scannedPkg;    }
---------------------------------------------------------------------------------------------------------------
在parsePackage的时候,会进行如下处理,如果是文件夹的话,会调用parseClusterPackage
如果不是文件夹的话,会进行parseMonolithicPackage的操作。
    public Package parsePackage(File packageFile, int flags) throws PackageParserException {        if (packageFile.isDirectory()) {            return parseClusterPackage(packageFile, flags);        } else {            return parseMonolithicPackage(packageFile, flags);        }    }
---------------------------------------------------------------------------------------------------------------
对于普通apk来说的话,是会进行parseMonolithicPackage的操作。然后我们来看一下这个函数的实现。
    @Deprecated    public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {        if (mOnlyCoreApps) { // mOnlyCoreApps的值是mOnlyCore,正常情况下也为false            final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);            if (!lite.coreApp) {                throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,                        "Not a coreApp: " + apkFile);            }        }        final AssetManager assets = new AssetManager(); // new了一个AssetManager的对象,AssetManager是资源管理框架        try {            final Package pkg = parseBaseApk(apkFile, assets, flags); // 在parseBaseApk的时候,会把assets传人            pkg.codePath = apkFile.getAbsolutePath();            return pkg;        } finally {            IoUtils.closeQuietly(assets);        }    }
----------------------------------------------------------------------------------------------------------------
    private Package parseBaseApk(File apkFile, AssetManager assets, int flags)            throws PackageParserException {        final String apkPath = apkFile.getAbsolutePath(); // 返回apkFile绝对路径名字字符串        mParseError = PackageManager.INSTALL_SUCCEEDED; // 在parse的开始,将mParseError的值设置为INSTALL_SUCCEEDED        mArchiveSourcePath = apkFile.getAbsolutePath();  // 返回apkFile的绝对路径的字符串        if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);        final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);        Resources res = null;        XmlResourceParser parser = null;        try {            res = new Resources(assets, mMetrics, null);            /*             Resources类的成员函数updateConfiguration首先是根据参数config和metrics来更新设备的当前配置信息,             例如,屏幕大小和密码、国家地区和语言、键盘配置情况等等,             接着再调用成员变量mAssets所指向的一个Java层的AssetManager对象的成员函数setConfiguration来将这些配置信息设置到与之关联的C++层的AssetManager对象中去。            */            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                    Build.VERSION.RESOURCES_SDK_INT);            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);            final String[] outError = new String[1];            final Package pkg = parseBaseApk(res, parser, flags, outError);            if (pkg == null) {                throw new PackageParserException(mParseError,                        apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);            }            pkg.baseCodePath = apkPath;            pkg.mSignatures = null;            return pkg;        } catch (PackageParserException e) {            throw e;        } catch (Exception e) {            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,                    "Failed to read manifest from " + apkPath, e);        } finally {            IoUtils.closeQuietly(parser);        }    }
---------------------------------------------------------------------------------------------------------------
    private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)            throws PackageParserException {        if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) {            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,                    "Invalid package file: " + apkPath);        }        // The AssetManager guarantees uniqueness for asset paths, so if this asset path        // already exists in the AssetManager, addAssetPath will only return the cookie        // assigned to it.        int cookie = assets.addAssetPath(apkPath); // addAssetPath 函数的作用是来将参数resDir所描述的Apk文件路径作为它的资源目录。        /* cookie有两种情况,分别为等不等于0. 如果等于0的话           AssetManager类的成员函数addAssetPath接着再检查在其成员变量mAssetPaths所描述的一个类型为asset_path的Vector中是否已经添加过参数path所描述的一个Apk文件路径了。           如果已经添加过了,那么AssetManager类的成员函数addAssetPath就不会再继续往下处理了,而是将与参数path所描述的一个Apk文件路径所对应的一个Cookie返回给调用者,即保存在输出参数cookie中,           前提是参数cookie的值不等于NULL。           一个Apk文件路径所对应的Cookie实际上只是一个整数,这个整数表示该Apk文件路径所对应的一个asset_path对象在成员变量mAssetPaths所描述的一个Vector中的索引再加上1。         */        if (cookie == 0) { // 等于0的时候,是添加vector失败的。所以会throw一个exception            throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,                    "Failed adding asset path: " + apkPath);        }        return cookie;    }

更多相关文章

  1. C语言函数以及函数的使用
  2. Android 获取本地音乐文件
  3. Android 4.0系统源码目录结构详解
  4. Android播放在线音乐文件
  5. Android应用程序调用系统解锁页面
  6. 文件读写
  7. Android读取Txt文件
  8. Android文件递归遍历
  9. android 不使用布局文件,完全由代码控制布局实例

随机推荐

  1. 显示json数组中的所有项目
  2. 图表。js数据数组使用PHP, MySQL。如何从
  3. Regexp exec列表的url由昏迷“,*”分隔
  4. JavaScript 最佳实践:帮你提升代码质量
  5. Vue js v-bind不工作?
  6. 如何在WebBrowser控件中注入Javascript ?
  7. 探讨FantasySoft遇到的有关Javascript的
  8. javascript数组和对象是否有设置顺序?
  9. 从另一个数组中删除数组的内容。
  10. 使用Jquery Ajax更改按钮的颜色(从外部PHP