zoukankan      html  css  js  c++  java
  • 《深入理解Android(卷2)》笔记 7.第四章 深入理解PackageManagerService

    知识点1:PKMS启动流程

    SystemServer.java::ServerThread::run--->PKMS::main--->PKMS::构造函数

    如下,在PKMS的main函数中将PKMS服务添加到到ServiceManager中。

    public static final IPackageManager main(Context context, boolean factoryTest,
        boolean onlyCore) { 
        PackageManagerService m = new PackageManagerService(context, factoryTest, onlyCore);
            ServiceManager.addService("package", m); 
            return m;
    }

    接下来对构造函数进行分析,这个是重点,将其分为3个大部分讨论。

    知识点2:PKMS之构造函数分析

    PKMS构造函数的主要功能是:扫描Android系统中几个目标文件夹中的APK,从而建立合适的数据结构以管理诸如package信息、四大组件信息、权限信息等。

    第1大部分:构造函数分析之前期准备

    将前期准备分为2个小部分,Settings类分析和xml文件扫描。先来看Settings类分析。

    第1大部分之第1小部分:Settings类分析

    PackageManagerService.java::构造函数
        public PackageManagerService(Context context, boolean factoryTest, boolean onlyCore) {
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                    SystemClock.uptimeMillis());
    
            //mSdkVersion的值取自系统属性ro.build.version.sdk,即编译的SDK版本。如果没有定义,则APK就不知道自己运行在Android哪个版本上。
            if (mSdkVersion <= 0) {
                Slog.w(TAG, "**** ro.build.version.sdk not set!");
            }
    
            mContext = context;
            mFactoryTest = factoryTest;
            mOnlyCore = onlyCore;
            //如果系统是eng版,则扫描package后,不对package做dex优化
            mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
            //mMetrics用于存储与显示屏相关的一些属性,比如屏幕宽/高/分辨率等信息
            mMetrics = new DisplayMetrics();
            //1.Settings是一个非常重要的类,该类用于存储系统运行过程中的一些设置
            mSettings = new Settings();
            mSettings.addSharedUserLPw("android.uid.system",
                    Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM);
            mSettings.addSharedUserLPw("android.uid.phone",
                    MULTIPLE_APPLICATION_UIDS
                            ? RADIO_UID : FIRST_APPLICATION_UID,
                    ApplicationInfo.FLAG_SYSTEM);
            mSettings.addSharedUserLPw("android.uid.log",
                    MULTIPLE_APPLICATION_UIDS
                            ? LOG_UID : FIRST_APPLICATION_UID,
                    ApplicationInfo.FLAG_SYSTEM);
            mSettings.addSharedUserLPw("android.uid.nfc",
                    MULTIPLE_APPLICATION_UIDS
                            ? NFC_UID : FIRST_APPLICATION_UID,
                    ApplicationInfo.FLAG_SYSTEM);
           mSettings.addSharedUserLPw("android.uid.fm_radio",
                    MULTIPLE_APPLICATION_UIDS
                            ? FM_RADIO_UID : FIRST_APPLICATION_UID,
                    ApplicationInfo.FLAG_SYSTEM);
    //-----------------第1大部分之第1小部分代码结束-----------------------

    第1大部分之第1小部分的代码主要是了解Settings类及其addSharedUserLPw函数。

    1.初识Settings

    先来看PKMS的构造函数中,使用Settings类的地方:

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

    接下来分析上述语句,两个知识点:(1)UID/GID的介绍;(2) addSharedUserLPw函数

    (1) UID/GID的介绍

    UID/GID和进程权限有关,系统定义的UID/GID在Process.java中。

    Process.java::UID/GID
    /**
         * Defines the UID/GID under which system code runs.
         */
        public static final int SYSTEM_UID = 1000;
    
        /**
         * Defines the UID/GID under which the telephony code runs.
         */
        public static final int PHONE_UID = 1001;
    
        /**
         * Defines the UID/GID under which the bluetooth code runs.
         * {@hide}
         */
        public static final int BLUETOOTH_UID = 1002;
    
        /**
         * Defines the UID/GID for the user shell.
         * @hide
         */
        public static final int SHELL_UID = 2000;
    
        /**
         * Defines the UID/GID for the log group.
         * @hide
         */
        public static final int LOG_UID = 1007;
    
        /**
         * Defines the UID/GID for the WIFI supplicant process.
         * @hide
         */
        public static final int WIFI_UID = 1010;
    
        /**
         * Defines the UID/GID for the mediaserver process.
         * @hide
         */
        public static final int MEDIA_UID = 1013;
    
        /**
         * Defines the GID for the group that allows write access to the SD card.
         * @hide
         */
        public static final int SDCARD_RW_GID = 1015;
    
        /**
         * Defines the UID/GID for the FM process.
         * @hide
         */
        public static final int FM_RADIO_UID = 1028;
    
        /**
         * Defines the UID/GID for the NFC service process.
         * @hide
         */
        public static final int NFC_UID = 1027;
    
        /**
         * Defines the GID for the group that allows write access to the internal media storage.
         * @hide
         */
        public static final int MEDIA_RW_GID = 1023;
    
        /**
         * Defines the start of a range of UIDs (and GIDs), going from this
         * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
         * to applications.
         */
        public static final int FIRST_APPLICATION_UID = 10000;
        /**
         * Last of application-specific UIDs starting at
         * {@link #FIRST_APPLICATION_UID}.
         */
        public static final int LAST_APPLICATION_UID = 99999;
    
        /**
         * Defines a secondary group id for access to the bluetooth hardware.
         */
        public static final int BLUETOOTH_GID = 2000;

    (2) addSharedUserLPw函数的分析

    addSharedUserLPw
    SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {
        //(2.1)mSharedUsers是一个HashMap,key为字符串,值为SharedUserSetting对象
            SharedUserSetting s = mSharedUsers.get(name);
            if (s != null) {
                if (s.userId == uid) {
                    return s;
                }
                PackageManagerService.reportSettingsProblem(Log.ERROR,
                        "Adding duplicate shared user, keeping first: " + name);
                return null;
            }
            //创建一个新的SharedUserSetting对象,并设置userId为uid
            s = new SharedUserSetting(name, pkgFlags);
            s.userId = uid;
            //(2.2)分析addUserIdLPw
            if (addUserIdLPw(uid, s, name)) {
                mSharedUsers.put(name, s);  //将name与s键值对添加到mSharedUsers中
                return s;
            }
            return null;
        }

    addSharedUserLPw函数中也有两个知识点:(2.1) SharedUserSetting类的分析;(2.2) addUserIdLPw函数的分析

    (2.1) SharedUserSetting类的分析

    该类包含了3个数据成员,

    final String name;
    int userId;
    final HashSet<PackageSetting> packages = new HashSet<PackageSetting>();

    这里,通过一个例子来分析SharedUserSetting的数据成员的作用。该例子来源于SystemUI的AndroidManifest.xml,如下所示:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="com.android.systemui"
            android:sharedUserId="android.uid.system"
            android:process="system"
            >

    在xml文件中声明了一个android:sharedUserId属性,值为"android.uid.system"。该属性有两个作用:

    * 两个或多个声明了同一种sharedUserId的APK可以共享彼此的数据,并可运行在同一进程中。

    * 声明特定的sharedUserId之后,该APK所在进程将被赋予指定的UID(即用户权限)。

    ps:要想实现”赋予指定的UID”,还需要在其Android.mk文件中声明额外的LOCAL_CERTIFICATE:=platform。

    通过以上分析,可以获得以下3个关键点,即三个数据成员的作用:

    * name(String):xml中sharedUserId属性指定了一个字符串,它是UID的字符串描述,故对应数据结构中也应该有一个字符串,这样就把代码和xml中的属性联系起来了。

    * userId(int):    linux系统中uid是一个整数,所以该数据结构中必然有一个整型变量。

    * packages(HashSet<PackageSetting>):多个Package可声明同一个sharedUserId,故该数据结构必然会保存那些声明了相同sharedUserId的package的某些信息。

    了解了3个关键点后,再看看Android系统如何设计相应数据结构的,SharedUserSetting类的关系图如下:

    分析关系图中的各个类之间的关系:

    * Settings类定义了一个mSharedUsers成员变量,它是一个HashMap,以字符串 (android.uid.system)为key,对应的value是一个SharedUserSetting对象。

    * SharedUserSetting继承自GrantedPermission,定义了一个packages成员变量,类型为HashSet,用于保存声明了相同SharedUserId的Package的权限设置信息。

    * 每个Package都有自己的权限设置。权限的概念由PackageSetting类表达。

    * Settings中还有两个成员变量。mUserIds和mOtherUserIds变量,类型分别为ArrayList和SparseArray。目的是以UID为索引,得到对应的SharedUserSetting对象。

    * 一般情况下,”以索引获取数组元素的速度”比”以key获取HashMap中的元素的速度”快得多。由此可知,这是以空间换取时间的做法。

    (2.2) addUserIdLPw函数的分析

    该函数的功能是将SharedUserSetting对象保存到对应的数组中。代码如下:

    addUserIdLPw
    private boolean addUserIdLPw(int uid, Object obj, Object name) {
        //Android中,应用apk所在进程的uid从10000开始,系统apk所在进程的uid小于1000
            if (uid >= PackageManagerService.FIRST_APPLICATION_UID + PackageManagerService.MAX_APPLICATION_UIDS) {
                return false;
            }
    
            if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
                int N = mUserIds.size();
                //计算索引,其值是uid和FIRST_APPLICATION_UID的差
                final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
                while (index >= N) {
                    mUserIds.add(null);
                    N++;
                }
                //判断该索引的值是否为空
                if (mUserIds.get(index) != null) {
                    PackageManagerService.reportSettingsProblem(Log.ERROR,
                            "Adding duplicate user id: " + uid
                            + " name=" + name);
                    return false;
                }
                mUserIds.set(index, obj); // mUserIds保存应用Package的uid
            } else {
                if (mOtherUserIds.get(uid) != null) {
                    PackageManagerService.reportSettingsProblem(Log.ERROR,
                            "Adding duplicate shared id: " + uid
                            + " name=" + name);
                    return false;
                }
                mOtherUserIds.put(uid, obj); // mOtherUserIds保存系统Package的uid
            }
            return true;
        }

    Setting的作用是管理Android系统运行过程中的一些设置信息。分析完毕。

    第1大部分之第2小部分:xml文件扫描

    1. xml文件扫描

    继续分析PKMS的构造函数:

    PKMS::构造函数
            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 = 0;
                mSeparateProcesses = null;
            }
            //创建一个Installer对象,该对象和Native进程installd交互
            mInstaller = new Installer();
            //得到一个WindowManager对象
            WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
            Display d = wm.getDefaultDisplay();
            d.getMetrics(mMetrics); //获取当前设备的显示屏信息
    
            synchronized (mInstallLock) {
            // writer
            synchronized (mPackages) {
                //该线程的工作是:程序的安装和卸载等
                mHandlerThread.start();
                mHandler = new PackageHandler(mHandlerThread.getLooper());
    
                File dataDir = Environment.getDataDirectory();
                // mAppDataDir指向/data/data目录
                mAppDataDir = new File(dataDir, "data");
                // mUserAppDataDir指向/data/user目录
                mUserAppDataDir = new File(dataDir, "user");
                // mDrmAppPrivateInstallDir指向/data/app-private目录
                mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
                //创建一个UserManager对象
                mUserManager = new UserManager(mInstaller, mUserAppDataDir);
    
                //(a)从文件中读权限
                readPermissions();
                //(b) readLPw分析
                mRestoredSettings = mSettings.readLPw();
                long startTime = SystemClock.uptimeMillis();
    //---------------------第1大部分之第2小部分代码结束---------------
    …...…...
        }

    接下来对代码中的(a)(b)进行分析。

    (a) readPermissions函数分析

    代码如下:

    readPermissions()
        void readPermissions() {
            //从"etc/permissions"读取权限,该目录中存储了和设备相关的一些权限信息。
            File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions");
            if (!libraryDir.exists() || !libraryDir.isDirectory()) {
                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
            for (File f : libraryDir.listFiles()) {
                // We'll read platform.xml last
                //先处理非platform.xml文件
                if (f.getPath().endsWith("etc/permissions/platform.xml")) {
                    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;
                }
                //调用readPermissionsFromXml函数解析此xml文件
                readPermissionsFromXml(f);
            }
    
            // Read permissions from .../etc/permissions/platform.xml last so it will take precedence
            final File permFile = new File(Environment.getRootDirectory(),
                    "etc/permissions/platform.xml");
            //解析platform.xml文件,该文件优先级最高
            readPermissionsFromXml(permFile);
        }

    可见,readPermissions函数就是调用readPermissionXml函数解析/system/etc/permissions目录下的文件。两个知识点:/system/etc/permissions下的xml文件和readPermissionXml函数。

    (a.1)先来分析手机上/system/etc/permissions目录下的platform.xml文件:

    platform.xml
    <permissions>
        <!—建立权限名和gid的映射关系-->
        <permission name="android.permission.BLUETOOTH_ADMIN" >
            <group gid="net_bt_admin" />
        </permission>
    
        <permission name="android.permission.BLUETOOTH" >
            <group gid="net_bt" />
        </permission>
    
        <permission name="android.permission.INTERNET" >
            <group gid="inet" />
        </permission>
    
        <permission name="android.permission.CAMERA" >
            <group gid="camera" />
        </permission>
    
        <permission name="android.permission.READ_LOGS" >
            <group gid="log" />
        </permission>
    
        <permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
            <group gid="sdcard_rw" />
        </permission>
    
        <!-- The group that /cache belongs to, linked to the permission
             set on the applications that can access /cache -->
        <permission name="android.permission.ACCESS_CACHE_FILESYSTEM" >
            <group gid="cache" />
        </permission>
    
        <!-- RW permissions to any system resources owned by group 'diag'.
             This is for carrier and manufacture diagnostics tools that must be
             installable from the framework. Be careful. -->
        <permission name="android.permission.DIAGNOSTIC" >
            <group gid="input" />
            <group gid="diag" />
        </permission>
    
        <!-- ================================= -->
    
    <!—赋予uid(shell所在线程)对应的权限-->
        <assign-permission name="android.permission.WRITE_EXTERNAL_STORAGE" uid="shell" />
        <assign-permission name="android.permission.SEND_SMS" uid="shell" />
        <assign-permission name="android.permission.CALL_PHONE" uid="shell" />
        <assign-permission name="android.permission.READ_CONTACTS" uid="shell" />
        <assign-permission name="android.permission.WRITE_CONTACTS" uid="shell" />
        <assign-permission name="android.permission.READ_CALENDAR" uid="shell" />
        <assign-permission name="android.permission.WRITE_CALENDAR" uid="shell" />
        <assign-permission name="android.permission.READ_USER_DICTIONARY" uid="shell" />
        <assign-permission name="android.permission.WRITE_USER_DICTIONARY" uid="shell" />
        <assign-permission name="android.permission.ACCESS_FINE_LOCATION" uid="shell" />
        <assign-permission name="android.permission.ACCESS_COARSE_LOCATION" uid="shell" />
        <assign-permission name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" uid="shell" />
        <assign-permission name="android.permission.ACCESS_NETWORK_STATE" uid="shell" />
        <assign-permission name="android.permission.ACCESS_WIFI_STATE" uid="shell" />
        <assign-permission name="android.permission.BLUETOOTH" uid="shell" />
        <!-- System tool permissions granted to the shell. -->
        <assign-permission name="android.permission.GET_TASKS" uid="shell" />
        <assign-permission name="android.permission.CHANGE_CONFIGURATION" uid="shell" />
        <assign-permission name="android.permission.REORDER_TASKS" uid="shell" />
        <assign-permission name="android.permission.SET_ANIMATION_SCALE" uid="shell" />
        <assign-permission name="android.permission.SET_PREFERRED_APPLICATIONS" uid="shell" />
        <assign-permission name="android.permission.WRITE_SETTINGS" uid="shell" />
        <assign-permission name="android.permission.WRITE_SECURE_SETTINGS" uid="shell" />
        <assign-permission name="android.permission.BROADCAST_STICKY" uid="shell" />
        <!-- Development tool permissions granted to the shell. -->
        <assign-permission name="android.permission.SET_DEBUG_APP" uid="shell" />
        <assign-permission name="android.permission.SET_PROCESS_LIMIT" uid="shell" />
        <assign-permission name="android.permission.SET_ALWAYS_FINISH" uid="shell" />
        <assign-permission name="android.permission.DUMP" uid="shell" />
        <assign-permission name="android.permission.SIGNAL_PERSISTENT_PROCESSES" uid="shell" />
        <!-- Internal permissions granted to the shell. -->
        <assign-permission name="android.permission.FORCE_BACK" uid="shell" />
        <assign-permission name="android.permission.BATTERY_STATS" uid="shell" />
        <assign-permission name="android.permission.INTERNAL_SYSTEM_WINDOW" uid="shell" />
        <assign-permission name="android.permission.INJECT_EVENTS" uid="shell" />
        <assign-permission name="android.permission.SET_ACTIVITY_WATCHER" uid="shell" />
        <assign-permission name="android.permission.READ_INPUT_STATE" uid="shell" />
        <assign-permission name="android.permission.SET_ORIENTATION" uid="shell" />
        <assign-permission name="android.permission.INSTALL_PACKAGES" uid="shell" />
        <assign-permission name="android.permission.CLEAR_APP_USER_DATA" uid="shell" />
        <assign-permission name="android.permission.DELETE_CACHE_FILES" uid="shell" />
        <assign-permission name="android.permission.DELETE_PACKAGES" uid="shell" />
        <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="shell" />
        <assign-permission name="android.permission.READ_FRAME_BUFFER" uid="shell" />
        <assign-permission name="android.permission.DEVICE_POWER" uid="shell" />
        <assign-permission name="android.permission.INSTALL_LOCATION_PROVIDER" uid="shell" />
        <assign-permission name="android.permission.BACKUP" uid="shell" />
    
        <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
        <assign-permission name="android.permission.ACCESS_DRM" uid="media" />
        <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
    
        <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
    
        <!—系统提供的Java库,应用程序运行时必须要链接这些库,该工作由系统自动完成-->
        <library name="android.test.runner"
                file="/system/framework/android.test.runner.jar" />
        <library name="javax.obex"
                file="/system/framework/javax.obex.jar"/>
    
    </permissions>

    platform.xml文件中主要由4个标签:

    *permission和group:用于建立Linux层和Android层permission之间的映射关系。

    *assign-permission:用于向指定的uid赋予相应的权限。

    *library:用于指定系统库。

    另外/system/etc/permissions目录下还有其他xml文件,包含feature标签。该标签的作用是用来描述一个手持终端应该支持的硬件特性。比如支持camera、支持蓝牙等。

    真实设备上/system/etc/permissions目录中的文件来源:在编译时阶段由不同硬件平台根据自己的配置信息复制相关文件(路径是/frameworks/base/data/etc/)到/system/etc/permissions目录中。编译完成后,将生成system镜像,最终烧到手机里,这就是来源。

    (a.2) readPermissionsFromXml分析

    作用:将xml文件中的标签以及它们之间的关系转换成代码中的相应的数据结构。

    readPermissionsFromXml
        private void readPermissionsFromXml(File permFile) {
            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);
    
                XmlUtils.beginDocument(parser, "permissions");
    
                while (true) {
                    XmlUtils.nextElement(parser);
                    if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
                        break;
                    }
    
                    String name = parser.getName();
                    //解析group标签
                    if ("group".equals(name)) {
                        String gidStr = parser.getAttributeValue(null, "gid");
                        if (gidStr != null) {
                            //转换xml中的gid字符串为整型,并保存到mGlobalGids中
                            int gid = Integer.parseInt(gidStr);
                            mGlobalGids = appendInt(mGlobalGids, gid);
                        } else {
                            Slog.w(TAG, "<group> without gid at "
                                    + parser.getPositionDescription());
                        }
    
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    } else if ("permission".equals(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();
                        //调用readPermission处理
                        readPermission(parser, perm);
                    //解析assign-permission标签
                    } else if ("assign-permission".equals(name)) {
                        String perm = parser.getAttributeValue(null, "name");
                        if (perm == null) {
                            Slog.w(TAG, "<assign-permission> without name at "
                                    + parser.getPositionDescription());
                            XmlUtils.skipCurrentTag(parser);
                            continue;
                        }
                        String uidStr = parser.getAttributeValue(null, "uid");
                        if (uidStr == null) {
                            Slog.w(TAG, "<assign-permission> without uid at "
                                    + parser.getPositionDescription());
                            XmlUtils.skipCurrentTag(parser);
                            continue;
                        }
                        //根据字符串uidstr,然后获得uid值
                        int uid = Process.getUidForName(uidStr);
                        if (uid < 0) {
                            Slog.w(TAG, "<assign-permission> with unknown uid \""
                                    + uidStr + "\" at "
                                    + parser.getPositionDescription());
                            XmlUtils.skipCurrentTag(parser);
                            continue;
                        }
                        perm = perm.intern();
                        //和assign相关的信息保存在mSystemPermissions中
                        HashSet<String> perms = mSystemPermissions.get(uid);
                        if (perms == null) {
                            perms = new HashSet<String>();
                            mSystemPermissions.put(uid, perms);
                        }
                        perms.add(perm);
                        XmlUtils.skipCurrentTag(parser);
    
                    } else if ("library".equals(name)) {  //解析library
                        String lname = parser.getAttributeValue(null, "name");
                        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); //将xml中的name和library保存到mSharedLibraries中
                        }
                        XmlUtils.skipCurrentTag(parser);
                        continue;
    
                    } else if ("feature".equals(name)) {  //解析feature标签
                        String fname = parser.getAttributeValue(null, "name");
                        if (fname == null) {
                            Slog.w(TAG, "<feature> without name at "
                                    + parser.getPositionDescription());
                        } else {
                            //Log.i(TAG, "Got feature " + fname);
                            //在xml中定义的feature由FeatureInfo表达
                            FeatureInfo fi = new FeatureInfo();
                            fi.name = fname;
                            mAvailableFeatures.put(fname, fi); //保存到mAvailableFeatures中
                        }
                        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);
            }
        }

    总结:主要还是了解各标签的作用;对于数据结构,知道就好~~

    (b) readLPw函数分析

    readLPw函数的功能也是解析文件,不过这些文件的内容却是在PKMS正常启动后生成的。

    这里介绍几个文件,具体位置在Settings构造函数中指明。代码如下: 

    Settings::构造函数
        Settings() {
            File dataDir = Environment.getDataDirectory();
            File systemDir = new File(dataDir, "system");
            systemDir.mkdirs();
            FileUtils.setPermissions(systemDir.toString(),
                    FileUtils.S_IRWXU|FileUtils.S_IRWXG
                    |FileUtils.S_IROTH|FileUtils.S_IXOTH,
                    -1, -1);
            /*
             packages.xml和packages-backup.xml为一组,用于描述系统中安装的Package的信息,其中backup为临时文件。
             packages-stopped.xml和packages-stopped-backup.xml为一组,用于描述系统中强制停止运行的Package的信息。
             package.list列出当前系统中应用级(uid>10000)Package的信息
    */
            mSettingsFilename = new File(systemDir, "packages.xml");
            mBackupSettingsFilename = new File(systemDir, "packages-backup.xml");
            mPackageListFilename = new File(systemDir, "packages.list");
            mStoppedPackagesFilename = new File(systemDir, "packages-stopped.xml");
            mBackupStoppedPackagesFilename = new File(systemDir, "packages-stopped-backup.xml");
        }

    文件的来历:

    *package.xml:PKMS扫描完目标文件后会创建该文件。当系统进行安装、卸载和更新等操作时,均会更新该文件。下次开机会直接从里面读取相关信息添加到内存相关列表中。

    *package-stopped.xml:该文件保存系统中被用户强制停止的Package的信息。

    *package.list:描述系统中存在的非系统自带的APK的信息。

    readLPw函数的功能就是解析这些xml文件的内容,然后建立并更新对应的数据结构。

    总结:”第1大部分:构造函数分析之前期准备”这一阶段的工作就到此为止了。在这个阶段的工作就是扫描并解析xml文件,将其中的信息保存到特定的数据结构中。

    这个阶段扫描的xml文件与权限以及上一次扫描得到的Package信息有关,它为PKMS下一阶段的工作提供了重要的参考信息。

    第2大部分:构造函数分析之扫描Package

    PKMS构造函数第二阶段的工作就是扫描系统中的APK了。由于需要逐个扫描文件,因此手机上装的APK越多,PKMS工作量就越大,系统启动速度就越慢。

    将扫描Package分为3个小部分,系统库的dex优化、扫描系统package、扫描非系统package。

    第2大部分之第1小部分:系统库的dex优化

    dex优化的主要工作是:检查 BOOTCLASSPATH,mSharedLibraries 及 /system/framework 下的jar是否需要dexopt,需要的话,则通过 dexopt 进行优化。

    接上一阶段的PKMS的构造函数,代码如下:

    PKMS::构造函数
                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.
                int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX; //定义扫描参数
                if (mNoDexOpt) {
                    Slog.w(TAG, "Running ENG build: no pre-dexopt!");
                    scanMode |= SCAN_NO_DEX; //在控制扫描过程中是否对APK文件进行dex优化
                }
    
                final HashSet<String> libFiles = new HashSet<String>();
                // mFrameworkDir指向/system/framework目录
                mFrameworkDir = new File(Environment.getRootDirectory(), "framework");
                // mDalvikCacheDir指向/data/dalvik-cache目录
                mDalvikCacheDir = new File(dataDir, "dalvik-cache");
    
                boolean didDexOpt = false;
    
                /*
                 Out of paranoia, ensure that everything in the boot class path has been dexed.
                 获取Java启动类库的路径,在init.rc文件中通过BOOTCLASSPATH环境变量输出,该变量指明了framwork所有核心库及文件位置。该值如下(init.rc中可找到):
    /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar:/system/framework/com.qrd.plugin.feature_query.jar:/system/framework/com.qrdinside.impl.jar
                 */
                String bootClassPath = System.getProperty("java.boot.class.path");
                if (bootClassPath != null) {
                    String[] paths = splitString(bootClassPath, ':');
                    for (int i=0; i<paths.length; i++) {
                        try {  //判断该Jar包是否需要重新做dex优化
                            if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) {
                                //将该Jar包文件路径保存到libFiles中,然后通过mInstall对象发送命令给installd,让其对该Jar包进行dex优化
                                libFiles.add(paths[i]);
                                mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true);
                                didDexOpt = true;
                            }
                        } catch (FileNotFoundException e) {
                            Slog.w(TAG, "Boot class path not found: " + paths[i]);
                        } catch (IOException e) {
                            Slog.w(TAG, "Cannot dexopt " + paths[i] + "; is it an APK or JAR? "
                                    + e.getMessage());
                        }
                    }
                } else {
                    Slog.w(TAG, "No BOOTCLASSPATH found!");
                }
    
                //判断系统库是否需要做dex优化。处理方式同上
                if (mSharedLibraries.size() > 0) {
                    Iterator<String> libs = mSharedLibraries.values().iterator();
                    while (libs.hasNext()) {
                        String lib = libs.next();
                        try {
                            if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
                                libFiles.add(lib);
                                mInstaller.dexopt(lib, Process.SYSTEM_UID, true);
                                didDexOpt = 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());
                        }
                    }
                }
    
                // Gross hack for now: we know this file doesn't contain any
                // code, so don't dexopt it to avoid the resulting log spew.
                // framework-res.apk定义了系统常用的资源,还有几个重要的Activity,如长按power键后弹出的选择框
                libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk");
    
                /**
                 * 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.
                 */
    
                //列举/system/framework目录中的文件
                String[] frameworkFiles = mFrameworkDir.list();
                if (frameworkFiles != null) {
                    //判断该目录下的APK或Jar文件是否需要做dex优化。处理方式同上
                    for (int i=0; i<frameworkFiles.length; i++) {
                        File libPath = new File(mFrameworkDir, frameworkFiles[i]);
                        String path = libPath.getPath();
                        // Skip the file if we alrady did it.
                        if (libFiles.contains(path)) {
                            continue;
                        }
                        // Skip the file if it is not a type we want to dexopt.
                        if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
                            continue;
                        }
                        try {
                            if (dalvik.system.DexFile.isDexOptNeeded(path)) {
                                mInstaller.dexopt(path, Process.SYSTEM_UID, true);
                                didDexOpt = true;
                            }
                        } catch (FileNotFoundException e) {
                            Slog.w(TAG, "Jar not found: " + path);
                        } catch (IOException e) {
                            Slog.w(TAG, "Exception reading jar: " + path, e);
                        }
                    }
                }
    
                /*
                上面代码对系统库(包括BOOTCLASSPATH指定的、platform.xml定义的、/system/framework目录下的Jar包与APK文件)进行一次仔细检查,对该优化的进行优化。如果优化期间,对任何一个文件都进行了优化,则设置didDexOpt为true
    */
                if (didDexOpt) {
                    // If we had to do a dexopt of one of the previous
                    // things, then something on the system has changed.
                    // Consider this significant, and wipe away all other
                    // existing dexopt files to ensure we don't leave any
                    // dangling around.
                    String[] files = mDalvikCacheDir.list();
                    if (files != null) {
                    /*如果前面对任意一个系统库重新做过dex优化,就需要删除cache文件。从删除cache的操作来看,这些cache文件应该是使用了dex优化后的系统库,当系统库重新做dex优化后,这些cache就失效了,需要删除。
                    */
                        for (int i=0; i<files.length; i++) {
                            String fn = files[i];
                            if (fn.startsWith("data@app@")
                                    || fn.startsWith("data@app-private@")) {
                                Slog.i(TAG, "Pruning dalvik file: " + fn);
                                (new File(mDalvikCacheDir, fn)).delete();
                            }
                        }
                    }
                }
                ............
    //-------------------系统库的dex优化到此为止----------------------

    第2大部分之第2小部分:扫描系统Package

    清空cache文件后,PKMS进入了这个阶段的核心内容,即扫描Package,代码如下:

    PKMS::构造函数
                // Find base frameworks (resource packages without code).
                //创建文件夹监控对象,监视/system/frameworks目录。利用了Linux平台的tify机制
                mFrameworkInstallObserver = new AppDirObserver(
                    mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
                mFrameworkInstallObserver.startWatching();
                //调用scanDirLI函数扫描/system/framework目录
                scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
                        | PackageParser.PARSE_IS_SYSTEM_DIR,
                        scanMode | SCAN_NO_DEX, 0);
                
                // Collect all system packages.
                //创建文件夹监控对象,监视/system/app目录
                mSystemAppDir = new File(Environment.getRootDirectory(), "app");
                mSystemInstallObserver = new AppDirObserver(
                    mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
                mSystemInstallObserver.startWatching();
                //扫描/system/app目录
                scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
                        | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
                
                // Collect all vendor packages.
    //监视并扫描/vendor/app目录
                mVendorAppDir = new File("/vendor/app");
                mVendorInstallObserver = new AppDirObserver(
                    mVendorAppDir.getPath(), OBSERVER_EVENTS, true);
                mVendorInstallObserver.startWatching();
                scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM
                        | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
    
                if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
                //和installd交互
                mInstaller.moveFiles();

    由以上代码可知,PKMS将扫描以下几个目录:

    * /system/framework:该目录中的文件都是系统库,例如framework.jar、services.jar、framework-res.apk。不过scanDirLI只扫描APK文件,所以framework-res.apk是该目录中唯一”受宠”的文件。
    * /system/app:该目录下全是默认的系统应用,如Browser.apk、Settings.apk等。
    * /vendor/app:该目录中的文件由厂商提供。

    PKMS调用scanDirLI函数进行扫描,接下来分析该函数。

    1.scanDirLI函数分析

    代码如下:

    scanDirLI
        private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
            String[] files = dir.list(); //列举该目录下的文件
            if (files == null) {
                Log.d(TAG, "No files in app dir " + dir);
                return;
            }
    
            if (DEBUG_PACKAGE_SCANNING) {
                Log.d(TAG, "Scanning app dir " + dir);
            }
    
            int i;
            for (i=0; i<files.length; i++) {
                File file = new File(dir, files[i]);
                if (!isPackageFilename(files[i])) {
                    // Ignore entries which are not apk's,即这里只扫描APK文件
                    continue;
                }
                /*
                调用scanPackageLI函数扫描一个特定的文件,返回值是PackageParser的内部类Package,该类的实例代表一个APK文件,所以它就是APK文件对应的数据结构
    */
                PackageParser.Package pkg = scanPackageLI(file,
                        flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);
                // Don't mess around with apps in system partition.
                if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                        mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
                    //注意flag的作用,只有非系统Package扫描失败,才会删除该文件
                    // Delete the apk
                    Slog.w(TAG, "Cleaning up failed install of " + file);
                    file.delete();
                }
            }
        }

    接着分析scanPackageLI函数。PKMS中有两个同名的scanPackageLI函数。先看初遇的scanPackageLI函数。

    2.初会scanPackageLI函数[-->PKMS:: scanPackageLI]

    PackageParser.Package::scanPackageLI
        private PackageParser.Package scanPackageLI(File scanFile,
                int parseFlags, int scanMode, long currentTime) {
            mLastScanError = PackageManager.INSTALL_SUCCEEDED;
            String scanPath = scanFile.getPath();
            parseFlags |= mDefParseFlags; //默认的扫描标志,正常情况下为0
            //创建一个PackageParser对象
            PackageParser pp = new PackageParser(scanPath);
            pp.setSeparateProcesses(mSeparateProcesses);  // mSeparateProcesses为空
            pp.setOnlyCoreApps(mOnlyCore); //mOnlyCore为false
    
            //调用parsePackage函数解析APK文件。注意,这里把mMetric对象也作为参数传递进去了
            final PackageParser.Package pkg = pp.parsePackage(scanFile,
                    scanPath, mMetrics, parseFlags);
            if (pkg == null) {
                mLastScanError = pp.getParseError();
                return null;
            }
            PackageSetting ps = null;
            PackageSetting updatedPkg;
    
    //--------BEGIN: 接下来主要是关于Package升级方面的工作-----------
            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.mDisabledSysPackages.get(
                        ps != null ? ps.name : pkg.packageName);
            }
            // 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 (pkg.mVersionCode < ps.versionCode) {
                        // The system package has been updated and the code path does not match
                        // Ignore entry. Skip it.
                        Log.i(TAG, "Package " + ps.name + " at " + scanFile
                                + " ignored: updated version " + ps.versionCode
                                + " better than this " + pkg.mVersionCode);
                        mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
                        return null;
                    } else {
                        // The current app on the system partion 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);
                        }
                        Slog.w(TAG, "Package " + ps.name + " at " + scanFile
                                + "reverting from " + ps.codePathString
                                + ": new version " + pkg.mVersionCode
                                + " better than installed " + ps.versionCode);
                        InstallArgs args = new FileInstallArgs(ps.codePathString,
                                ps.resourcePathString, ps.nativeLibraryPathString);
                        args.cleanUpResourcesLI();
                        mSettings.enableSystemPackageLPw(ps.name);
                    }
                }
            }
            if (updatedPkg != null) {
                // An updated system app will not have the PARSE_IS_SYSTEM flag set initially
                parseFlags |= PackageParser.PARSE_IS_SYSTEM;
            }
            //--------END: 关于Package升级方面的工作结束-----------
    
            // Verify certificates against what was last scanned
            if (!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)) {
                Slog.w(TAG, "Failed verifying certificates for package:" + pkg.packageName);
                return null;
            }
            // The apk is forward locked (not public) if its code and resources are kept in different files.目前只有/vendor/app目录下的扫描会使用该PARSE_FORWARD_LOCK标志。
            // TODO grab this value from PackageSettings
            if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
                parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
            }
    
            String codePath = null;
            String resPath = null;
            if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0) {
                if (ps != null && ps.resourcePathString != null) {
                    resPath = ps.resourcePathString;
                } else {
                    // Should not happen at all. Just log an error.
                    Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
                }
            } else {
                resPath = pkg.mScanPath;
            }
            codePath = pkg.mScanPath; //mScanPath指向该APK文件所在的位置
            // Set application objects path explicitly.其中codePath和resPath都指向APK文件所在位置
            setApplicationInfoPaths(pkg, codePath, resPath);
            // Note that we invoke the following method only if we are about to unpack an application
            //调用第二个scanPackageLI函数
            return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime);
        }

    scanPackageLI函数首先调用PackageParser类的相关函数(parsePackage函数)对APK文件进行解析。PackageParser类完成了从物理文件到对应数据结构的转换。下面来分析它。

    3.PackageParser分析

    PackageParser主要负责APK文件的解析,即解析APK文件中的AndroidManifest.xml文件。代码如下:[-->PackageParser.java::parsePackage]

    parsePackage
        public Package parsePackage(File sourceFile, String destCodePath,
                DisplayMetrics metrics, int flags) {
            mParseError = PackageManager.INSTALL_SUCCEEDED;
    
            mArchiveSourcePath = sourceFile.getPath();
            if (!sourceFile.isFile()) {
                Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);
                mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
                return null;
            }
            if (!isPackageFilename(sourceFile.getName())
                    && (flags&PARSE_MUST_BE_APK) != 0) {
                if ((flags&PARSE_IS_SYSTEM) == 0) {
                    // We expect to have non-.apk files in the system dir,
                    // so don't warn about them.
                    Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);
                }
                mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
                return null;
            }
    
            if (DEBUG_JAR)
                Slog.d(TAG, "Scanning package: " + mArchiveSourcePath);
    
            //检查是否为APK文件
            XmlResourceParser parser = null;
            AssetManager assmgr = null;
            Resources res = null;
            boolean assetError = true;
            try {
                assmgr = new AssetManager();
                int cookie = assmgr.addAssetPath(mArchiveSourcePath);
                if (cookie != 0) {
                    res = new Resources(assmgr, metrics, null);
                    assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                            Build.VERSION.RESOURCES_SDK_INT);
            //获得一个xml资源解析对象,该对象解析的是APK中的AndroidManifest.xml
                    parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
                    assetError = false;
                } else {
                    Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
                }
            } catch (Exception e) {
                Slog.w(TAG, "Unable to read AndroidManifest.xml of "
                        + mArchiveSourcePath, e);
            }
            if (assetError) {
                if (assmgr != null) assmgr.close();
                mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
                return null;
            }
            //出错处理
            String[] errorText = new String[1];
            Package pkg = null;
            Exception errorException = null;
            try {
                // 调用另外一个parsePackage函数
                pkg = parsePackage(res, parser, flags, errorText);
            } catch (Exception e) {
                errorException = e;
                mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
            }
    
    
            if (pkg == null) {
                // If we are only parsing core apps, then a null with INSTALL_SUCCEEDED
                // just means to skip this app so don't make a fuss about it.
                if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {
                    if (errorException != null) {
                        Slog.w(TAG, mArchiveSourcePath, errorException);
                    } else {
                        Slog.w(TAG, mArchiveSourcePath + " (at "
                                + parser.getPositionDescription()
                                + "): " + errorText[0]);
                    }
                    if (mParseError == PackageManager.INSTALL_SUCCEEDED) {
                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    }
                }
                parser.close();
                assmgr.close();
                return null;
            }
    
            parser.close();
            assmgr.close();
    
            // 设置代码和资源路径,都指向APK文件所在的位置
            pkg.mPath = destCodePath;
            pkg.mScanPath = mArchiveSourcePath;
            pkg.mSignatures = null;
    
            return pkg;
        }

    该函数中调用了另一个同名的parsePackage函数。这个同名函数的作用单一,就是解析AndroidManifest.xml中的各种标签。分析其中的关键代码:

    parsePackage
        private Package parsePackage(
            Resources res, XmlResourceParser parser, int flags, String[] outError)
            throws XmlPullParserException, IOException {
            AttributeSet attrs = parser;
    
            mParseInstrumentationArgs = null;
            mParseActivityArgs = null;
            mParseServiceArgs = null;
            mParseProviderArgs = null;
            
            //得到Package的名字,其实就是得到AndroidManifest.xml中的Package属性的值,每个APK都必须定义该属性
            ..........
            int type;
            ..........
            //以pkgName名字为参数,创建一个Package对象,后面的工作就是解析xml并填充该Package信息
            final Package pkg = new Package(pkgName);
            boolean foundApp = false;
            
            TypedArray sa = res.obtainAttributes(attrs,
                    com.android.internal.R.styleable.AndroidManifest);
            pkg.mVersionCode = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
            pkg.mVersionName = sa.getNonConfigurationString(
                    com.android.internal.R.styleable.AndroidManifest_versionName, 0);
            if (pkg.mVersionName != null) {
                pkg.mVersionName = pkg.mVersionName.intern();
            }
            String str = sa.getNonConfigurationString(
                    com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
            if (str != null && str.length() > 0) {
                String nameError = validateName(str, true);
                if (nameError != null && !"android".equals(pkgName)) {
                    outError[0] = "<manifest> specifies bad sharedUserId name \""
                        + str + "\": " + nameError;
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
                    return null;
                }
                pkg.mSharedUserId = str.intern();
                pkg.mSharedUserLabel = sa.getResourceId(
                        com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
            }
            sa.recycle();
    
            pkg.installLocation = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifest_installLocation,
                    PARSE_DEFAULT_INSTALL_LOCATION);
            pkg.applicationInfo.installLocation = pkg.installLocation;
            
            // Resource boolean are -1, so 1 means we don't know the value.
            int supportsSmallScreens = 1;
            int supportsNormalScreens = 1;
            int supportsLargeScreens = 1;
            int supportsXLargeScreens = 1;
            int resizeable = 1;
            int anyDensity = 1;
            
            int outerDepth = parser.getDepth();
            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 (tagName.equals("application")) {
                    if (foundApp) {
                        if (RIGID_PARSER) {
                            outError[0] = "<manifest> has more than one <application>";
                            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                            return null;
                        } else {
                            Slog.w(TAG, "<manifest> has more than one <application>");
                            XmlUtils.skipCurrentTag(parser);
                            continue;
                        }
                    }
    
                    foundApp = true;
                    //解析application标签
                    if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
                        return null;
                    }
                } else if (tagName.equals("permission-group")) {
                    //解析permission-group标签
                    if (parsePermissionGroup(pkg, res, parser, attrs, outError) == null) {
                        return null;
                    }
                } else if (tagName.equals("permission")) {
                    //解析permission标签
                    if (parsePermission(pkg, res, parser, attrs, outError) == null) {
                        return null;
                    }
                } else if (tagName.equals("permission-tree")) {
                    if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
                        return null;
                    }
                } else if (tagName.equals("uses-permission")) {
                    //从xml文件中获取uses-permission标签的属性
                    sa = res.obtainAttributes(attrs,
                            com.android.internal.R.styleable.AndroidManifestUsesPermission);
    
                    // Note: don't allow this value to be a reference to a resource
                    // that may change.
                    //取出属性值,也就是对应的权限使用声明
                    String name = sa.getNonResourceString(
                            com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
    
                    sa.recycle();
    
                    if (name != null && !pkg.requestedPermissions.contains(name)) {
                        //添加到Package的requestedPermissions数组
                        pkg.requestedPermissions.add(name.intern());
                    }
    
                    XmlUtils.skipCurrentTag(parser);
    
                } else if (tagName.equals("uses-configuration")) {
                    ConfigurationInfo cPref = new ConfigurationInfo();
                    sa = res.obtainAttributes(attrs,
                            com.android.internal.R.styleable.AndroidManifestUsesConfiguration);
                    cPref.reqTouchScreen = sa.getInt(
                            com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
                            Configuration.TOUCHSCREEN_UNDEFINED);
                    cPref.reqKeyboardType = sa.getInt(
                            com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
                            Configuration.KEYBOARD_UNDEFINED);
                    if (sa.getBoolean(
                            com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
                            false)) {
                        cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
                    }
                    cPref.reqNavigation = sa.getInt(
                            com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
                            Configuration.NAVIGATION_UNDEFINED);
                    if (sa.getBoolean(
                            com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
                            false)) {
                        cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
                    }
                    sa.recycle();
                    pkg.configPreferences.add(cPref);//保存到configPreference数组
    
                    XmlUtils.skipCurrentTag(parser);
    
                } else if (tagName.equals("uses-feature")) {
                    FeatureInfo fi = new FeatureInfo();
                    sa = res.obtainAttributes(attrs,
                            com.android.internal.R.styleable.AndroidManifestUsesFeature);
                    // Note: don't allow this value to be a reference to a resource
                    // that may change.
                    fi.name = sa.getNonResourceString(
                            com.android.internal.R.styleable.AndroidManifestUsesFeature_name);
                    if (fi.name == null) {
                        fi.reqGlEsVersion = sa.getInt(
                                com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
                                FeatureInfo.GL_ES_VERSION_UNDEFINED);
                    }
                    if (sa.getBoolean(
                            com.android.internal.R.styleable.AndroidManifestUsesFeature_required,
                            true)) {
                        fi.flags |= FeatureInfo.FLAG_REQUIRED;
                    }
                    sa.recycle();
                    if (pkg.reqFeatures == null) {
                        pkg.reqFeatures = new ArrayList<FeatureInfo>();
                    }
                    pkg.reqFeatures.add(fi);
                    
                    if (fi.name == null) {
                        ConfigurationInfo cPref = new ConfigurationInfo();
                        cPref.reqGlEsVersion = fi.reqGlEsVersion;
                        pkg.configPreferences.add(cPref);
                    }
    
                    XmlUtils.skipCurrentTag(parser);
    
                } else if (tagName.equals("uses-sdk")) {
                    if (SDK_VERSION > 0) {
                        sa = res.obtainAttributes(attrs,
                                com.android.internal.R.styleable.AndroidManifestUsesSdk);
    
                        int minVers = 0;
                        String minCode = null;
                        int targetVers = 0;
                        String targetCode = null;
                        
                        TypedValue val = sa.peekValue(
                                com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
                        if (val != null) {
                            if (val.type == TypedValue.TYPE_STRING && val.string != null) {
                                targetCode = minCode = val.string.toString();
                            } else {
                                // If it's not a string, it's an integer.
                                targetVers = minVers = val.data;
                            }
                        }
                        
                        val = sa.peekValue(
                                com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
                        if (val != null) {
                            if (val.type == TypedValue.TYPE_STRING && val.string != null) {
                                targetCode = minCode = val.string.toString();
                            } else {
                                // If it's not a string, it's an integer.
                                targetVers = val.data;
                            }
                        }
                        
                        sa.recycle();
    
                        if (minCode != null) {
                            if (!minCode.equals(SDK_CODENAME)) {
                                if (SDK_CODENAME != null) {
                                    outError[0] = "Requires development platform " + minCode
                                            + " (current platform is " + SDK_CODENAME + ")";
                                } else {
                                    outError[0] = "Requires development platform " + minCode
                                            + " but this is a release platform.";
                                }
                                mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                                return null;
                            }
                        } else if (minVers > SDK_VERSION) {
                            outError[0] = "Requires newer sdk version #" + minVers
                                    + " (current version is #" + SDK_VERSION + ")";
                            mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                            return null;
                        }
                        
                        if (targetCode != null) {
                            if (!targetCode.equals(SDK_CODENAME)) {
                                if (SDK_CODENAME != null) {
                                    outError[0] = "Requires development platform " + targetCode
                                            + " (current platform is " + SDK_CODENAME + ")";
                                } else {
                                    outError[0] = "Requires development platform " + targetCode
                                            + " but this is a release platform.";
                                }
                                mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                                return null;
                            }
                            // If the code matches, it definitely targets this SDK.
                            pkg.applicationInfo.targetSdkVersion
                                    = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
                        } else {
                            pkg.applicationInfo.targetSdkVersion = targetVers;
                        }
                    }
    
                    XmlUtils.skipCurrentTag(parser);
    
                } else if (tagName.equals("supports-screens")) {
                    sa = res.obtainAttributes(attrs,
                            com.android.internal.R.styleable.AndroidManifestSupportsScreens);
    
                    pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger(
                            com.android.internal.R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp,
                            0);
                    pkg.applicationInfo.compatibleWidthLimitDp = sa.getInteger(
                            com.android.internal.R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp,
                            0);
                    pkg.applicationInfo.largestWidthLimitDp = sa.getInteger(
                            com.android.internal.R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp,
                            0);
    
                    // This is a trick to get a boolean and still able to detect
                    // if a value was actually set.
                    supportsSmallScreens = sa.getInteger(
                            com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens,
                            supportsSmallScreens);
                    supportsNormalScreens = sa.getInteger(
                            com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens,
                            supportsNormalScreens);
                    supportsLargeScreens = sa.getInteger(
                            com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,
                            supportsLargeScreens);
                    supportsXLargeScreens = sa.getInteger(
                            com.android.internal.R.styleable.AndroidManifestSupportsScreens_xlargeScreens,
                            supportsXLargeScreens);
                    resizeable = sa.getInteger(
                            com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable,
                            resizeable);
                    anyDensity = sa.getInteger(
                            com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity,
                            anyDensity);
    
                    sa.recycle();
                    
                    XmlUtils.skipCurrentTag(parser);
                    
                } else if (tagName.equals("protected-broadcast")) {
                    sa = res.obtainAttributes(attrs,
                            com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);
    
                    // Note: don't allow this value to be a reference to a resource
                    // that may change.
                    String name = sa.getNonResourceString(
                            com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);
    
                    sa.recycle();
    
                    if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
                        if (pkg.protectedBroadcasts == null) {
                            pkg.protectedBroadcasts = new ArrayList<String>();
                        }
                        if (!pkg.protectedBroadcasts.contains(name)) {
                            pkg.protectedBroadcasts.add(name.intern());
                        }
                    }
    
                    XmlUtils.skipCurrentTag(parser);
                    
                } else if (tagName.equals("instrumentation")) {
                    if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
                        return null;
                    }
                    
                } else if (tagName.equals("original-package")) {
                    sa = res.obtainAttributes(attrs,
                            com.android.internal.R.styleable.AndroidManifestOriginalPackage);
    
                    String orig =sa.getNonConfigurationString(
                            com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
                    if (!pkg.packageName.equals(orig)) {
                        if (pkg.mOriginalPackages == null) {
                            pkg.mOriginalPackages = new ArrayList<String>();
                            pkg.mRealPackage = pkg.packageName;
                        }
                        pkg.mOriginalPackages.add(orig);
                    }
    
                    sa.recycle();
    
                    XmlUtils.skipCurrentTag(parser);
                    
                } else if (tagName.equals("adopt-permissions")) {
                    sa = res.obtainAttributes(attrs,
                            com.android.internal.R.styleable.AndroidManifestOriginalPackage);
    
                    String name = sa.getNonConfigurationString(
                            com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
    
                    sa.recycle();
    
                    if (name != null) {
                        if (pkg.mAdoptPermissions == null) {
                            pkg.mAdoptPermissions = new ArrayList<String>();
                        }
                        pkg.mAdoptPermissions.add(name);
                    }
    
                    XmlUtils.skipCurrentTag(parser);
                    
                } else if (tagName.equals("uses-gl-texture")) {
                    // Just skip this tag
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                    
                } else if (tagName.equals("compatible-screens")) {
                    // Just skip this tag
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                    
                } else if (tagName.equals("eat-comment")) {
                    // Just skip this tag
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                    
                } else if (RIGID_PARSER) {
                    outError[0] = "Bad element under <manifest>: "
                        + parser.getName();
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return null;
    
                } else {
                    Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
                            + " at " + mArchiveSourcePath + " "
                            + parser.getPositionDescription());
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                }
            }
    
            if (!foundApp && pkg.instrumentation.size() == 0) {
                outError[0] = "<manifest> does not contain an <application> or <instrumentation>";
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
            }
    
            final int NP = PackageParser.NEW_PERMISSIONS.length;
            StringBuilder implicitPerms = null;
            for (int ip=0; ip<NP; ip++) {
                final PackageParser.NewPermissionInfo npi
                        = PackageParser.NEW_PERMISSIONS[ip];
                if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) {
                    break;
                }
                if (!pkg.requestedPermissions.contains(npi.name)) {
                    if (implicitPerms == null) {
                        implicitPerms = new StringBuilder(128);
                        implicitPerms.append(pkg.packageName);
                        implicitPerms.append(": compat added ");
                    } else {
                        implicitPerms.append(' ');
                    }
                    implicitPerms.append(npi.name);
                    pkg.requestedPermissions.add(npi.name);
                }
            }
            if (implicitPerms != null) {
                Slog.i(TAG, implicitPerms.toString());
            }
            
            if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
                    && pkg.applicationInfo.targetSdkVersion
                            >= android.os.Build.VERSION_CODES.DONUT)) {
                pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
            }
            if (supportsNormalScreens != 0) {
                pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
            }
            if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
                    && pkg.applicationInfo.targetSdkVersion
                            >= android.os.Build.VERSION_CODES.DONUT)) {
                pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
            }
            if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
                    && pkg.applicationInfo.targetSdkVersion
                            >= android.os.Build.VERSION_CODES.GINGERBREAD)) {
                pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
            }
            if (resizeable < 0 || (resizeable > 0
                    && pkg.applicationInfo.targetSdkVersion
                            >= android.os.Build.VERSION_CODES.DONUT)) {
                pkg.applicationInfo.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
            }
            if (anyDensity < 0 || (anyDensity > 0
                    && pkg.applicationInfo.targetSdkVersion
                            >= android.os.Build.VERSION_CODES.DONUT)) {
                pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
            }
    
            return pkg;
        }

    上面介绍了PackageParser类对APK的解析流程,即对AndroidManifest.xml文件的解析流程。

    现在来看一下PackageParser类内部的重要信息:

    * 这些内部类主要是用来保存对应的信息。解析AndroidManifest.xml文件得到的信息由Package类保存。四大组件的信息也由对应的类保存。因为一个APK可声明多个组件,所以activities和services等都可以声明为ArrayList。

    * 各种组件类还需要创造出XXIntentInfo类(最终继承自IntentFilter类)而不是直接使用XXInfo类,主要原因是为了支持Intent匹配查询。该类会判断自己是否满足某Intent中的Action等的要求,若满足,则返回对应的ActivityInfo。

    * PackageLite仅用来存储Package的一些简单信息。在安装Package时,会用到该类。

    4.再会scanPackageLI(函数重载,不是之前遇到的那个函数)

    当PackageParse扫描完一个APK后,此时系统已经根据该APK中的AndroidManifest.xml创建了一个完整的Package对象,下一步就是将该Package对象加入到系统中。此时调用的函数就是另一个scanPackageLI,代码如下:

    PackageParser.Package::scanPackageLI
        private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
                int parseFlags, int scanMode, long currentTime) {
            File scanFile = new File(pkg.mScanPath);
    ......
            mScanningPath = scanFile;
            //设置Package对象中的applicationInfo的flags标签,用于标示该Package为系统Package。
            if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
                pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
            }
            //单独判断包名是否为”android”,很重要!!!
            if (pkg.packageName.equals("android")) {
                synchronized (mPackages) {
                    if (mAndroidApplication != null) {
                        Slog.w(TAG, 
                    ......
                    // mPlatformPackage成员用于保存包名为”android”的Package信息。
                    mPlatformPackage = pkg;
                    pkg.mVersionCode = mSdkVersion;
                    // mAndroidApplication保存此Package中的ApplicationInfo。
                    mAndroidApplication = pkg.applicationInfo;
                    // mResolveActivity指向表示ChooseActivity信息的ActivityInfo。
                    mResolveActivity.applicationInfo = mAndroidApplication;
                    mResolveActivity.name = ResolverActivity.class.getName();
                    mResolveActivity.packageName = mAndroidApplication.packageName;
                    mResolveActivity.processName = mAndroidApplication.processName;
                    mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
                    mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
                    mResolveActivity.theme = com.android.internal.R.style.Theme_Holo_Dialog_Alert;
                    mResolveActivity.exported = true;
                    mResolveActivity.enabled = true;
                    // mResolveInfo的ActivityInfo成员指向mResolveActivity。它用于存储系统解析Intent(经IntentFilter过滤)后得到的结果信息。比如满足某个Intent的Activity信息
                    mResolveInfo.activityInfo = mResolveActivity;
                    mResolveInfo.priority = 0;
                    mResolveInfo.preferredOrder = 0;
                    mResolveInfo.match = 0;
                    mResolveComponentName = new ComponentName(
                            mAndroidApplication.packageName, mResolveActivity.name);
                }
            }

    这里先分析下上述if (pkg.packageName.equals("android")) 这个判断条件,即单独判断PackageName为”android”的Package。与该Package对应的是framework-res.apk。

    实际上,framework-res.apk还包含几个常用的Activity。

    *ChooserActivity:当多个Activity符合某个Intent时,系统会弹出该Activity,由用户选择。

    *RingtonePickerActivity:铃声选择Activity。

    *ShutdownActivity:关机前弹出的选择对话框。

    此处保存这些信息,主要是为了提高运行过程中的效率。

    ps:从PKMS中查询满足某个Intent的Activity时,返回的就是ResolveInfo,再根据ResolveInfo的信息得到具体的Activity。

    继续分析scanPackageLI函数。

    scanPackageLI
            //mPackage用于保存系统内的所有Package,以packageName为key
            if (mPackages.containsKey(pkg.packageName)
                    || mSharedLibraries.containsKey(pkg.packageName)) {
                Slog.w(TAG, "Application package " + pkg.packageName
                        + " already installed.  Skipping duplicate.");
                mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
                return null;
            }
    
            // Initialize package source and resource directories
            File destCodeFile = new File(pkg.applicationInfo.sourceDir);
            File destResourceFile = new File(pkg.applicationInfo.publicSourceDir);
    
            SharedUserSetting suid = null;
            PackageSetting pkgSetting = null;
    
            if (!isSystemApp(pkg)) {
                // Only system apps can use these features.
                pkg.mOriginalPackages = null;
                pkg.mRealPackage = null;
                pkg.mAdoptPermissions = null;
            }
    
            // writer
            synchronized (mPackages) {
                /*此处代码很长,概括其工作内容如下:
                1.如果该Package声明了useslibraries,那么系统要判断library是否在mShareLibraries中
                2.如果package声明了SharedUser,则需要处理SharedUserSettings相关内容,由Settings的getSharedUserLPw函数处理
                3.处理pkgSetting,通过调用Settings的getPackageLPw函数完成
                4.调用verifySignaturesLP函数,检查该Package的signature
                */
                if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
                    if (mTmpSharedLibraries == null ||
                            mTmpSharedLibraries.length < mSharedLibraries.size()) {
                        mTmpSharedLibraries = new String[mSharedLibraries.size()];
                    }
                    int num = 0;
                    int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
                    for (int i=0; i<N; i++) {
                        final String file = mSharedLibraries.get(pkg.usesLibraries.get(i));
                        if (file == null) {
                            Slog.e(TAG, "Package " + pkg.packageName
                                    + " requires unavailable shared library "
                                    + pkg.usesLibraries.get(i) + "; failing!");
                            mLastScanError = PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
                            return null;
                        }
                        mTmpSharedLibraries[num] = file;
                        num++;
                    }
                    N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0;
                    for (int i=0; i<N; i++) {
                        final String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
                        if (file == null) {
                            Slog.w(TAG, "Package " + pkg.packageName
                                    + " desires unavailable shared library "
                                    + pkg.usesOptionalLibraries.get(i) + "; ignoring!");
                        } else {
                            mTmpSharedLibraries[num] = file;
                            num++;
                        }
                    }
                    if (num > 0) {
                        pkg.usesLibraryFiles = new String[num];
                        System.arraycopy(mTmpSharedLibraries, 0,
                                pkg.usesLibraryFiles, 0, num);
                    }
                }
    
                if (pkg.mSharedUserId != null) {
                    suid = mSettings.getSharedUserLPw(pkg.mSharedUserId,
                            pkg.applicationInfo.flags, true);
                    if (suid == null) {
                        Slog.w(TAG, "Creating application package " + pkg.packageName
                                + " for shared user failed");
                        mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                        return null;
                    }
                    if (DEBUG_PACKAGE_SCANNING) {
                        if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
                            Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId
                                    + "): packages=" + suid.packages);
                    }
                }
                
                // Check if we are renaming from an original package name.
                PackageSetting origPackage = null;
                String realName = null;
                if (pkg.mOriginalPackages != null) {
                    // This package may need to be renamed to a previously
                    // installed name.  Let's check on that...
                    final String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage);
                    if (pkg.mOriginalPackages.contains(renamed)) {
                        // This package had originally been installed as the
                        // original name, and we have already taken care of
                        // transitioning to the new one.  Just update the new
                        // one to continue using the old name.
                        realName = pkg.mRealPackage;
                        if (!pkg.packageName.equals(renamed)) {
                            // Callers into this function may have already taken
                            // care of renaming the package; only do it here if
                            // it is not already done.
                            pkg.setPackageName(renamed);
                        }
                        
                    } else {
                        for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) {
                            if ((origPackage = mSettings.peekPackageLPr(
                                    pkg.mOriginalPackages.get(i))) != null) {
                                // We do have the package already installed under its
                                // original name...  should we use it?
                                if (!verifyPackageUpdateLPr(origPackage, pkg)) {
                                    // New package is not compatible with original.
                                    origPackage = null;
                                    continue;
                                } else if (origPackage.sharedUser != null) {
                                    // Make sure uid is compatible between packages.
                                    if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {
                                        Slog.w(TAG, "Unable to migrate data from " + origPackage.name
                                                + " to " + pkg.packageName + ": old uid "
                                                + origPackage.sharedUser.name
                                                + " differs from " + pkg.mSharedUserId);
                                        origPackage = null;
                                        continue;
                                    }
                                } else {
                                    if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
                                            + pkg.packageName + " to old name " + origPackage.name);
                                }
                                break;
                            }
                        }
                    }
                }
                
                if (mTransferedPackages.contains(pkg.packageName)) {
                    Slog.w(TAG, "Package " + pkg.packageName
                            + " was transferred to another, but its .apk remains");
                }
                
                // Just create the setting, don't add it yet. For already existing packages
                // the PkgSetting exists already and doesn't have to be created.
                pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
                        destResourceFile, pkg.applicationInfo.nativeLibraryDir,
                        pkg.applicationInfo.flags, true, false);
                if (pkgSetting == null) {
                    Slog.w(TAG, "Creating application package " + pkg.packageName + " failed");
                    mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                    return null;
                }
                
                if (pkgSetting.origPackage != null) {
                    // If we are first transitioning from an original package,
                    // fix up the new package's name now.  We need to do this after
                    // looking up the package under its new name, so getPackageLP
                    // can take care of fiddling things correctly.
                    pkg.setPackageName(origPackage.name);
                    
                    // File a report about this.
                    String msg = "New package " + pkgSetting.realName
                            + " renamed to replace old package " + pkgSetting.name;
                    reportSettingsProblem(Log.WARN, msg);
                    
                    // Make a note of it.
                    mTransferedPackages.add(origPackage.name);
                    
                    // No longer need to retain this.
                    pkgSetting.origPackage = null;
                }
                
                if (realName != null) {
                    // Make a note of it.
                    mTransferedPackages.add(pkg.packageName);
                }
                
                if (mSettings.mDisabledSysPackages.get(pkg.packageName) != null) {
                    pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
                }
    
                pkg.applicationInfo.uid = pkgSetting.userId;
                pkg.mExtras = pkgSetting;
    
                if (!verifySignaturesLP(pkgSetting, pkg)) {
                    if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                        return null;
                    }
                    // The signature has changed, but this package is in the system
                    // image...  let's recover!
                    pkgSetting.signatures.mSignatures = pkg.mSignatures;
                    // However...  if this package is part of a shared user, but it
                    // doesn't match the signature of the shared user, let's fail.
                    // What this means is that you can't change the signatures
                    // associated with an overall shared user, which doesn't seem all
                    // that unreasonable.
                    if (pkgSetting.sharedUser != null) {
                        if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
                                pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
                            Log.w(TAG, "Signature mismatch for shared user : " + pkgSetting.sharedUser);
                            mLastScanError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
                            return null;
                        }
                    }
                    // File a report about this.
                    String msg = "System package " + pkg.packageName
                            + " signature changed; retaining data.";
                    reportSettingsProblem(Log.WARN, msg);
                }
    
                // Verify that this new package doesn't have any content providers
                // that conflict with existing packages.  Only do this if the
                // package isn't already installed, since we don't want to break
                // things that are installed.
                if ((scanMode&SCAN_NEW_INSTALL) != 0) {
                    final int N = pkg.providers.size();
                    int i;
                    for (i=0; i<N; i++) {
                        PackageParser.Provider p = pkg.providers.get(i);
                        if (p.info.authority != null) {
                            String names[] = p.info.authority.split(";");
                            for (int j = 0; j < names.length; j++) {
                                if (mProviders.containsKey(names[j])) {
                                    PackageParser.Provider other = mProviders.get(names[j]);
                                    Slog.w(TAG, "Can't install because provider name " + names[j] +
                                            " (in package " + pkg.applicationInfo.packageName +
                                            ") is already used by "
                                            + ((other != null && other.getComponentName() != null)
                                                    ? other.getComponentName().getPackageName() : "?"));
                                    mLastScanError = PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
                                    return null;
                                }
                            }
                        }
                    }
                }
    
                if (pkg.mAdoptPermissions != null) {
                    // This package wants to adopt ownership of permissions from
                    // another package.
                    for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
                        final String origName = pkg.mAdoptPermissions.get(i);
                        final PackageSetting orig = mSettings.peekPackageLPr(origName);
                        if (orig != null) {
                            if (verifyPackageUpdateLPr(orig, pkg)) {
                                Slog.i(TAG, "Adopting permissions from " + origName + " to "
                                        + pkg.packageName);
                                mSettings.transferPermissionsLPw(origName, pkg.packageName);
                            }
                        }
                    }
                }
            }
            
            final String pkgName = pkg.packageName;
            
            final long scanFileTime = scanFile.lastModified();
            final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0;
            //确定运行该package的进程的进程名,一般用packageName作为进程名
            pkg.applicationInfo.processName = fixProcessName(
                    pkg.applicationInfo.packageName,
                    pkg.applicationInfo.processName,
                    pkg.applicationInfo.uid);
    
            File dataPath;
            if (mPlatformPackage == pkg) {
                // The system package is special.
                dataPath = new File (Environment.getDataDirectory(), "system");
                pkg.applicationInfo.dataDir = dataPath.getPath();
            } else {
                // 返回该package的目录,一般是/data/data/packageName/
                dataPath = getDataPathForPackage(pkg.packageName, 0);
                
                boolean uidError = false;
                
                if (dataPath.exists()) { //如果该目录已存在,则要处理uid的问题
                    mOutPermissions[1] = 0;
                    FileUtils.getPermissions(dataPath.getPath(), mOutPermissions);
    
                    // If we have mismatched owners for the data path, we have a problem.
                    if (mOutPermissions[1] != pkg.applicationInfo.uid) {
                        boolean recovered = false;
                        if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
                            // If this is a system app, we can at least delete its
                            // current data so the application will still work.
                            int ret = mInstaller.remove(pkgName, 0);
                            if (ret >= 0) {
                                // TODO: Kill the processes first
                                // Remove the data directories for all users
                                mUserManager.removePackageForAllUsers(pkgName);
                                // Old data gone!
                                String msg = "System package " + pkg.packageName
                                        + " has changed from uid: "
                                        + mOutPermissions[1] + " to "
                                        + pkg.applicationInfo.uid + "; old data erased";
                                reportSettingsProblem(Log.WARN, msg);
                                recovered = true;
    
                                // And now re-install the app.
                                ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
                                        pkg.applicationInfo.uid);
                                if (ret == -1) {
                                    // Ack should not happen!
                                    msg = "System package " + pkg.packageName
                                            + " could not have data directory re-created after delete.";
                                    reportSettingsProblem(Log.WARN, msg);
                                    mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                                    return null;
                                }
                                // Create data directories for all users
                                mUserManager.installPackageForAllUsers(pkgName,
                                        pkg.applicationInfo.uid);
                            }
                            if (!recovered) {
                                mHasSystemUidErrors = true;
                            }
                        }
                        if (!recovered) {
                            pkg.applicationInfo.dataDir = "/mismatched_uid/settings_"
                                + pkg.applicationInfo.uid + "/fs_"
                                + mOutPermissions[1];
                            pkg.applicationInfo.nativeLibraryDir = pkg.applicationInfo.dataDir;
                            String msg = "Package " + pkg.packageName
                                    + " has mismatched uid: "
                                    + mOutPermissions[1] + " on disk, "
                                    + pkg.applicationInfo.uid + " in settings";
                            // writer
                            synchronized (mPackages) {
                                mSettings.mReadMessages.append(msg);
                                mSettings.mReadMessages.append('\n');
                                uidError = true;
                                if (!pkgSetting.uidError) {
                                    reportSettingsProblem(Log.ERROR, msg);
                                }
                            }
                        }
                    }
                    pkg.applicationInfo.dataDir = dataPath.getPath();
                } else {
                    //向installd发送install命令,实际上就是在/data/data目录下建立packageName目录。
                    if (DEBUG_PACKAGE_SCANNING) {
                        if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
                            Log.v(TAG, "Want this data dir: " + dataPath);
                    }
                    //invoke installer to do the actual installation
                    int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
                            pkg.applicationInfo.uid);
                    if (ret < 0) {
                        // Error from installer
                        mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                        return null;
                    }
                    // 为系统所有user安装此程序
                    mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid);
    
                    if (dataPath.exists()) {
                        pkg.applicationInfo.dataDir = dataPath.getPath();
                    } else {
                        Slog.w(TAG, "Unable to create data directory: " + dataPath);
                        pkg.applicationInfo.dataDir = null;
                    }
                }
    
                /*
                 * Set the data dir to the default "/data/data/<package name>/lib"
                 * if we got here without anyone telling us different (e.g., apps
                 * stored on SD card have their native libraries stored in the ASEC
                 * container with the APK).
                 *
                 * This happens during an upgrade from a package settings file that
                 * doesn't have a native library path attribute at all.
                 */
                if (pkg.applicationInfo.nativeLibraryDir == null && pkg.applicationInfo.dataDir != null) {
    //为该Package确定native library所在目录,一般是/data/data/packageName/lib
                    if (pkgSetting.nativeLibraryPathString == null) {
                        final String nativeLibraryPath = new File(dataPath, LIB_DIR_NAME).getPath();
                        pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath;
                        pkgSetting.nativeLibraryPathString = nativeLibraryPath;
                    } else {
                        pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString;
                    }
                }
    
                pkgSetting.uidError = uidError;
            }
    
            String path = scanFile.getPath();
            /* Note: We don't want to unpack the native binaries for
             *        system applications, unless they have been updated
             *        (the binaries are already under /system/lib).
             *        Also, don't unpack libs for apps on the external card
             *        since they should have their libraries in the ASEC
             *        container already.
             *
             *        In other words, we're going to unpack the binaries
             *        only for non-system apps and system app upgrades.
             */
            //如果该APK包含了native动态库,则需要将它们从APK文件中解压并复制到对应目录中
            if (pkg.applicationInfo.nativeLibraryDir != null) {
                try {
                    final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
                    final String dataPathString = dataPath.getCanonicalPath();
                    //从Android 2.3开始,系统package的native库统一放在/system/lib下。所以系统不会提取系统Package目录下的APK包中的native库
                    if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
                        /*
                         * Upgrading from a previous version of the OS sometimes
                         * leaves native libraries in the /data/data/<app>/lib
                         * directory for system apps even when they shouldn't be.
                         * Recent changes in the JNI library search path
                         * necessitates we remove those to match previous behavior.
                         */
                        if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) {
                            Log.i(TAG, "removed obsolete native libraries for system package "
                                    + path);
                        }
                    } else if (nativeLibraryDir.getParentFile().getCanonicalPath()
                            .equals(dataPathString)) {
                        /*
                         * Make sure the native library dir isn't a symlink to
                         * something. If it is, ask installd to remove it and create
                         * a directory so we can copy to it afterwards.
                         */
                        boolean isSymLink;
                        try {
                            isSymLink = S_ISLNK(Libcore.os.lstat(nativeLibraryDir.getPath()).st_mode);
                        } catch (ErrnoException e) {
                            // This shouldn't happen, but we'll fail-safe.
                            isSymLink = true;
                        }
                        //判断是否为链接,如果是,需要删除该链接
                        if (isSymLink) {
                            mInstaller.unlinkNativeLibraryDirectory(dataPathString);
                        }
    
                        /*
                         * If this is an internal application or our
                         * nativeLibraryPath points to our data directory, unpack
                         * the libraries if necessary.
                         */
                        //在lib下建立和CPU类型对应的目录,例如AMR平台的是arm/,MIPS平台的是mips/
                        NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir);
                    } else {
                        Slog.i(TAG, "Linking native library dir for " + path);
                        mInstaller.linkNativeLibraryDirectory(dataPathString,
                                pkg.applicationInfo.nativeLibraryDir);
                    }
                } catch (IOException ioe) {
                    Log.e(TAG, "Unable to get canonical file " + ioe.toString());
                }
            }
            pkg.mScanPath = path;
    
            if ((scanMode&SCAN_NO_DEX) == 0) {
                //对该APK做dex优化
                if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0)
                        == DEX_OPT_FAILED) {
                    mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
                    return null;
                }
            }
    
            if (mFactoryTest && pkg.requestedPermissions.contains(
                    android.Manifest.permission.FACTORY_TEST)) {
                pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
            }
    
            // Request the ActivityManager to kill the process(only for existing packages)
            // so that we do not end up in a confused state while the user is still using the older
            // version of the application while the new one gets installed.
            //如果该APK已经存在,要先杀掉运行该APK的进程
            if ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                killApplication(pkg.applicationInfo.packageName,
                            pkg.applicationInfo.uid);
            }
    
            //在此之前,四大组件的信息都属于Package的私有财产,现在需要把它们登记注册到PKMS内部的财产管理对象中。这样PKMS就可对外提供统一的组件信息,而不必拘泥于具体的Package
            synchronized (mPackages) {
                // We don't expect installation to fail beyond this point,
                if ((scanMode&SCAN_MONITOR) != 0) {
                    mAppDirs.put(pkg.mPath, pkg);
                }
                // Add the new setting to mSettings
                mSettings.insertPackageSettingLPw(pkgSetting, pkg);
                // Add the new setting to mPackages
                mPackages.put(pkg.applicationInfo.packageName, pkg);
                // Make sure we don't accidentally delete its data.
                mSettings.mPackagesToBeCleaned.remove(pkgName);
                
                // Take care of first install / last update times.
                if (currentTime != 0) {
                    if (pkgSetting.firstInstallTime == 0) {
                        pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
                    } else if ((scanMode&SCAN_UPDATE_TIME) != 0) {
                        pkgSetting.lastUpdateTime = currentTime;
                    }
                } else if (pkgSetting.firstInstallTime == 0) {
                    // We need *something*.  Take time time stamp of the file.
                    pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
                } else if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
                    if (scanFileTime != pkgSetting.timeStamp) {
                        // A package on the system image has changed; consider this
                        // to be an update.
                        pkgSetting.lastUpdateTime = scanFileTime;
                    }
                }
                //处理该Package中的provider信息
                int N = pkg.providers.size();
                StringBuilder r = null;
                int i;
                for (i=0; i<N; i++) {
                    PackageParser.Provider p = pkg.providers.get(i);
                    p.info.processName = fixProcessName(pkg.applicationInfo.processName,
                            p.info.processName, pkg.applicationInfo.uid);
                    // mProvidersByComponent提供基于ComponentName的Provider信息查询
                    mProvidersByComponent.put(new ComponentName(p.info.packageName,
                            p.info.name), p);
                    p.syncable = p.info.isSyncable;
                    if (p.info.authority != null) {
                        String names[] = p.info.authority.split(";");
                        p.info.authority = null;
                        for (int j = 0; j < names.length; j++) {
                            if (j == 1 && p.syncable) {
                                // We only want the first authority for a provider to possibly be
                                // syncable, so if we already added this provider using a different
                                // authority clear the syncable flag. We copy the provider before
                                // changing it because the mProviders object contains a reference
                                // to a provider that we don't want to change.
                                // Only do this for the second authority since the resulting provider
                                // object can be the same for all future authorities for this provider.
                                p = new PackageParser.Provider(p);
                                p.syncable = false;
                            }
                            if (!mProviders.containsKey(names[j])) {
                                mProviders.put(names[j], p);
                                if (p.info.authority == null) {
                                    p.info.authority = names[j];
                                } else {
                                    p.info.authority = p.info.authority + ";" + names[j];
                                }
                                if (DEBUG_PACKAGE_SCANNING) {
                                    if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
                                        Log.d(TAG, "Registered content provider: " + names[j]
                                                + ", className = " + p.info.name + ", isSyncable = "
                                                + p.info.isSyncable);
                                }
                            } else {
                                PackageParser.Provider other = mProviders.get(names[j]);
                                Slog.w(TAG, "Skipping provider name " + names[j] +
                                        " (in package " + pkg.applicationInfo.packageName +
                                        "): name already used by "
                                        + ((other != null && other.getComponentName() != null)
                                                ? other.getComponentName().getPackageName() : "?"));
                            }
                        }
                    }
                    if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                        if (r == null) {
                            r = new StringBuilder(256);
                        } else {
                            r.append(' ');
                        }
                        r.append(p.info.name);
                    }
                }
                if (r != null) {
                    if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Providers: " + r);
                }
    
                //处理该Package中的service信息
                N = pkg.services.size();
                r = null;
                for (i=0; i<N; i++) {
                    PackageParser.Service s = pkg.services.get(i);
                    s.info.processName = fixProcessName(pkg.applicationInfo.processName,
                            s.info.processName, pkg.applicationInfo.uid);
                    mServices.addService(s);
                    if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                        if (r == null) {
                            r = new StringBuilder(256);
                        } else {
                            r.append(' ');
                        }
                        r.append(s.info.name);
                    }
                }
                if (r != null) {
                    if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Services: " + r);
                }
    
                //处理该Package中的receiver信息
                N = pkg.receivers.size();
                r = null;
                for (i=0; i<N; i++) {
                    PackageParser.Activity a = pkg.receivers.get(i);
                    a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                            a.info.processName, pkg.applicationInfo.uid);
                    mReceivers.addActivity(a, "receiver");
                    if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                        if (r == null) {
                            r = new StringBuilder(256);
                        } else {
                            r.append(' ');
                        }
                        r.append(a.info.name);
                    }
                }
                if (r != null) {
                    if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Receivers: " + r);
                }
    
                //处理该Package中的Activity信息
                N = pkg.activities.size();
                r = null;
                for (i=0; i<N; i++) {
                    PackageParser.Activity a = pkg.activities.get(i);
                    a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                            a.info.processName, pkg.applicationInfo.uid);
                    mActivities.addActivity(a, "activity");
                    if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                        if (r == null) {
                            r = new StringBuilder(256);
                        } else {
                            r.append(' ');
                        }
                        r.append(a.info.name);
                    }
                }
                if (r != null) {
                    if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Activities: " + r);
                }
    
                N = pkg.permissionGroups.size();
                r = null;
                for (i=0; i<N; i++) {
                    PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);
                    PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name);
                    if (cur == null) {
                        mPermissionGroups.put(pg.info.name, pg);
                        if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                            if (r == null) {
                                r = new StringBuilder(256);
                            } else {
                                r.append(' ');
                            }
                            r.append(pg.info.name);
                        }
                    } else {
                        Slog.w(TAG, "Permission group " + pg.info.name + " from package "
                                + pg.info.packageName + " ignored: original from "
                                + cur.info.packageName);
                        if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                            if (r == null) {
                                r = new StringBuilder(256);
                            } else {
                                r.append(' ');
                            }
                            r.append("DUP:");
                            r.append(pg.info.name);
                        }
                    }
                }
                if (r != null) {
                    if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Permission Groups: " + r);
                }
    
                N = pkg.permissions.size();
                r = null;
                for (i=0; i<N; i++) {
                    PackageParser.Permission p = pkg.permissions.get(i);
                    HashMap<String, BasePermission> permissionMap =
                            p.tree ? mSettings.mPermissionTrees
                            : mSettings.mPermissions;
                    p.group = mPermissionGroups.get(p.info.group);
                    if (p.info.group == null || p.group != null) {
                        BasePermission bp = permissionMap.get(p.info.name);
                        if (bp == null) {
                            bp = new BasePermission(p.info.name, p.info.packageName,
                                    BasePermission.TYPE_NORMAL);
                            permissionMap.put(p.info.name, bp);
                        }
                        if (bp.perm == null) {
                            if (bp.sourcePackage == null
                                    || bp.sourcePackage.equals(p.info.packageName)) {
                                BasePermission tree = findPermissionTreeLP(p.info.name);
                                if (tree == null
                                        || tree.sourcePackage.equals(p.info.packageName)) {
                                    bp.packageSetting = pkgSetting;
                                    bp.perm = p;
                                    bp.uid = pkg.applicationInfo.uid;
                                    if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                                        if (r == null) {
                                            r = new StringBuilder(256);
                                        } else {
                                            r.append(' ');
                                        }
                                        r.append(p.info.name);
                                    }
                                } else {
                                    Slog.w(TAG, "Permission " + p.info.name + " from package "
                                            + p.info.packageName + " ignored: base tree "
                                            + tree.name + " is from package "
                                            + tree.sourcePackage);
                                }
                            } else {
                                Slog.w(TAG, "Permission " + p.info.name + " from package "
                                        + p.info.packageName + " ignored: original from "
                                        + bp.sourcePackage);
                            }
                        } else if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                            if (r == null) {
                                r = new StringBuilder(256);
                            } else {
                                r.append(' ');
                            }
                            r.append("DUP:");
                            r.append(p.info.name);
                        }
                        if (bp.perm == p) {
                            bp.protectionLevel = p.info.protectionLevel;
                        }
                    } else {
                        Slog.w(TAG, "Permission " + p.info.name + " from package "
                                + p.info.packageName + " ignored: no group "
                                + p.group);
                    }
                }
                if (r != null) {
                    if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Permissions: " + r);
                }
    
                N = pkg.instrumentation.size();
                r = null;
                for (i=0; i<N; i++) {
                    PackageParser.Instrumentation a = pkg.instrumentation.get(i);
                    a.info.packageName = pkg.applicationInfo.packageName;
                    a.info.sourceDir = pkg.applicationInfo.sourceDir;
                    a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir;
                    a.info.dataDir = pkg.applicationInfo.dataDir;
                    a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
                    mInstrumentation.put(a.getComponentName(), a);
                    if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                        if (r == null) {
                            r = new StringBuilder(256);
                        } else {
                            r.append(' ');
                        }
                        r.append(a.info.name);
                    }
                }
                if (r != null) {
                    if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Instrumentation: " + r);
                }
    
                if (pkg.protectedBroadcasts != null) {
                    N = pkg.protectedBroadcasts.size();
                    for (i=0; i<N; i++) {
                        mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
                    }
                }
    
                pkgSetting.setTimeStamp(scanFileTime);
            }
            //到此,Package的私有财产完成了公有化改造。
            return pkg;

    这个函数的代码好长好长啊...............................................终于over了~~~

    5.scanDirLI函数总结

    scanDirLI函数用于对指定目录下的APK文件进行扫描,该函数的工作流程如下图:

    扫描完APK文件后,Package的私有财产就充公了。PKMS提供了几个重要数据结构来保存这些财产。

     

    第2大部分之第3小部分:扫描非系统Package

    非系统Package是指那些不存储在系统目录下的APK文件,在构造函数中的代码如下: 

    PKMS::构造函数
                // Prune any system packages that no longer exist.
                if (!mOnlyCore) {
                    Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
                    while (psit.hasNext()) {
                        //删除系统package中那些不存在的APK
                        PackageSetting ps = psit.next();
                        if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0
                                && !mPackages.containsKey(ps.name)
                                && !mSettings.mDisabledSysPackages.containsKey(ps.name)) {
                            psit.remove();
                            String msg = "System package " + ps.name
                                    + " no longer exists; wiping its data";
                            reportSettingsProblem(Log.WARN, msg);
                            mInstaller.remove(ps.name, 0);
                            mUserManager.removePackageForAllUsers(ps.name);
                        }
                    }
                }
                
                mAppInstallDir = new File(dataDir, "app");
                //删除安装不成功的文件及临时文件
                ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
                //clean up list
                for(int i = 0; i < deletePkgsList.size(); i++) {
                    //clean up here
                    cleanupInstallFailedPackage(deletePkgsList.get(i));
                }
                //delete tmp files
                deleteTempPackageFiles();
    
                if (!mOnlyCore) {
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis());
    //在普通模式下,还需要扫描/data/app以及/data/app_private目录
                    mAppInstallObserver = new AppDirObserver(
                        mAppInstallDir.getPath(), OBSERVER_EVENTS, false);
                    mAppInstallObserver.startWatching();
                    scanDirLI(mAppInstallDir, 0, scanMode, 0);
        
                    mDrmAppInstallObserver = new AppDirObserver(
                        mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);
                    mDrmAppInstallObserver.startWatching();
                    scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
                            scanMode, 0);
                } else {
                    mAppInstallObserver = null;
                    mDrmAppInstallObserver = null;
                }

    系统中存放APK文件的目录:

    *系统Package目录包括:/system/frameworks、/system/app和/vendor/app。

    *非系统Package目录包括:/data/app、/data/app-private。

    第2大部分的总结:PKMS在这个阶段的任务十分繁重,操作繁琐,而且还要创建比较多的对象,所以它是一个耗时耗内存的操作。作者认为没有较好的方案的原因可能是因为APK之间存在一些依赖关系,导致不好优化。其实我觉得,系统的APK不存在依赖非系统的APK的情况,所以完全可以将非系统的APK的信息保存在文件中,启动时直接读取文件,这样就可以节省一些时间。

    第3大部分:构造函数分析之扫尾工作

    扫尾工作是:将第2大部分收集的信息再集中整理一次,比如将这些信息保存到文件中。代码如下: 

    PKMS::构造函数
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                        SystemClock.uptimeMillis());
                Slog.i(TAG, "Time to scan packages: "
                        + ((SystemClock.uptimeMillis()-startTime)/1000f)
                        + " seconds");
    
                // If the platform SDK has changed since the last time we booted,
                // we need to re-grant app permission to catch any new ones that
                // appear.  This is really a hack, and means that apps can in some
                // cases get permissions that the user didn't initially explicitly
                // allow...  it would be nice to have some better way to handle
                // this situation.
                final boolean regrantPermissions = mSettings.mInternalSdkPlatform
                        != mSdkVersion;
                if (regrantPermissions) Slog.i(TAG, "Platform changed from "
                        + mSettings.mInternalSdkPlatform + " to " + mSdkVersion
                        + "; regranting permissions for internal storage");
                mSettings.mInternalSdkPlatform = mSdkVersion;
                
                //汇总并更新和Permission相关的信息
                updatePermissionsLPw(null, null, true, regrantPermissions, regrantPermissions);
    
                // 将信息写到package.xml、package.list及package-stopped.xml文件中
                mSettings.writeLPr();
    
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                        SystemClock.uptimeMillis());
    
                // Now after opening every single application zip, make sure they
                // are all flushed.  Not really needed, but keeps things nice and
                // tidy.
                Runtime.getRuntime().gc();
    
                mRequiredVerifierPackage = getRequiredVerifierLPr();
            } // synchronized (mPackages)
            } // synchronized (mInstallLock)
        }

    对PKMS构造函数的分析就到底为止了~~~~终于结束了!!!

    知识点3:APK Installation分析(分析APK的安装及相关处理流程)

    1.adb install分析

    adb install有多个参数,举个例子,这里分析adb install frameworktest.apk。adb是一个命令,install是它的参数。此处直接跳转到处理Install参数的代码:

    [--->commandline.c::adb_commandline]

    adb_commandline
    int adb_commandline(int argc, char **argv)
    {
        ......
        if(!strcmp(argv[0], "install")) {
            if (argc < 2) return usage();
            //调用Install_app函数处理
            return install_app(ttype, serial, argc, argv);
        }
        ......
    }

    [--->commandline.c::install_app] 

    install_app
    int install_app(transport_type transport, char* serial, int argc, char** argv)
    {
        //要安装的APK现在还在Host机器上,要先把APK复制到手机中。
    //这里需要设置复制目标的目录,如果安装在内部存储中,则目标目录为/data/local/tmp;
    //如果安装在SD卡上,则目标目录为/sdcard/tmp。
        static const char *const DATA_DEST = "/data/local/tmp/%s";
        static const char *const SD_DEST = "/sdcard/tmp/%s";
        const char* where = DATA_DEST;
        char apk_dest[PATH_MAX];
        char verification_dest[PATH_MAX];
        char* apk_file;
        char* verification_file = NULL;
        int file_arg = -1;
        int err;
        int i;
    
        for (i = 1; i < argc; i++) {
            if (*argv[i] != '-') {
                file_arg = i;
                break;
            } else if (!strcmp(argv[i], "-i")) {
                // Skip the installer package name.
                i++;
            } else if (!strcmp(argv[i], "-s")) {
                where = SD_DEST; //-s参数指明该APK安装到SD卡上
            }
        }
    
        if (file_arg < 0) {
            fprintf(stderr, "can't find filename in arguments\n");
            return 1;
        } else if (file_arg + 2 < argc) {
            fprintf(stderr, "too many files specified; only takes APK file and verifier file\n");
            return 1;
        }
    
        apk_file = argv[file_arg];
    
        if (file_arg != argc - 1) {
            verification_file = argv[file_arg + 1];
        }
    
        if (check_file(apk_file) || check_file(verification_file)) {
            return 1;
        }
    
        snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
        if (verification_file != NULL) {
            snprintf(verification_dest, sizeof(verification_dest), where, get_basename(verification_file));
    
            if (!strcmp(apk_dest, verification_dest)) {
                fprintf(stderr, "APK and verification file can't have the same name\n");
                return 1;
            }
        }
    
        //获取目标文件的全路径,如果安装到内部存储中,则目标全路径为/data/local/tmp/安装包名,调用do_sync_push函数将此APK传送到手机的目标路径
        err = do_sync_push(apk_file, apk_dest, 1 /* verify APK */);
        if (err) {
            return err;
        } else {
            argv[file_arg] = apk_dest; /* destination name, not source location */
        }
    
        //(a) Android 4.0新增了一个安装过程中的Verification功能。稍后介绍。
        if (verification_file != NULL) {
            err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */);
            if (err) {
                goto cleanup_apk;
            } else {
                argv[file_arg + 1] = verification_dest; /* destination name, not source location */
            }
        }
        //(b) 执行pm命令。有意思!
        pm_command(transport, serial, argc, argv);
    
        if (verification_file != NULL) {
            delete_file(transport, serial, verification_dest);
        }
    
    cleanup_apk:
        //(c) 在手机中执行shell rm命令,删除刚才传送过去的目标APK文件。why?
        delete_file(transport, serial, apk_dest);
    
        return err;
    }

    分析代码中的(a)(b)(c)三个关键点:

    (a) 这个新增的Verification功能其实就是在安装时,把相关信息发送到指定的Verification程序(另外一个APK),由它对要安装的APK进行检查(Verify)。

    (b) 调用pm_command进行安装,在下面分析。

    (c) 为什么删除呢?因为PKMS在安装过程中会将该APK复制一份到/data/app目录下,所以/data/local/tmp目录下的对应文件就可以删除了。

    2.pm分析 

    pm_command
    static int pm_command(transport_type transport, char* serial,
                          int argc, char** argv)
    {
        char buf[4096];
        snprintf(buf, sizeof(buf), "shell:pm");
    
        while(argc-- > 0) {
            char *quoted;
            quoted = dupAndQuote(*argv++);
            strncat(buf, " ", sizeof(buf)-1);
            strncat(buf, quoted, sizeof(buf)-1);
            free(quoted);
        }
        //发送”shell:pm install”参数给手机端的adbd
        send_shellcommand(transport, serial, buf);
        return 0;
    }

    手机端的adbd在收到客户端发来的shell pm命令时会启动一个shell,然后在其中执行pm。那pm到底是什么?为什么可以在shell下执行?

    实际上,pm是一个脚本。

    [--->/frameworks/base/cmds/pm/pm]

    pm
    # Script to start "pm" on the device, which has a very rudimentary
    # shell.
    #
    base=/system
    export CLASSPATH=$base/framework/pm.jar
    exec app_process $base/bin com.android.commands.pm.Pm "$@"

    在编译system.image时,Android.mk中会将该脚本复制到system/bin目录下。分析pm脚本克制,它就是通过app_process执行pm.jar包的main函数。

    分析pm.java,app_process执行的就是它定义的main函数,它相当于java函数的入口函数。 

    main() / run()
        public static void main(String[] args) {
            new Pm().run(args);
    }
    
        //直接分析run函数
        public void run(String[] args) {
            boolean validCommand = false;
            ......
            mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
            ......
            mArgs = args;
            String op = args[0];
            mNextArg = 1;
            ......
            if ("install".equals(op)) {
                runInstall();
                return;
            }
            ......
        }

    接下来分析runInstall函数,代码如下: 

    runInstall()
        private void runInstall() {
            int installFlags = 0;
            String installerPackageName = null;
    
            String opt;
            while ((opt=nextOption()) != null) {
                if (opt.equals("-l")) {
                    installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
                } else if (opt.equals("-r")) {
                    installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
                } else if (opt.equals("-i")) {
                    installerPackageName = nextOptionData();
                    ......
                } ......
    
            final Uri apkURI;
            final Uri verificationURI;
    
            // Populate apkURI, must be present
            final String apkFilePath = nextArg();
            System.err.println("\tpkg: " + apkFilePath);
            if (apkFilePath != null) {
                apkURI = Uri.fromFile(new File(apkFilePath));
            } else {
                System.err.println("Error: no package specified");
                showUsage();
                return;
            }
    
            // Populate verificationURI, optionally present
            //获取Verification Package的文件位置
            final String verificationFilePath = nextArg();
            if (verificationFilePath != null) {
                System.err.println("\tver: " + verificationFilePath);
                verificationURI = Uri.fromFile(new File(verificationFilePath));
            } else {
                verificationURI = null;
            }
    
            //创建PackageInstallObserver,用于接收PKMS的安装结果
            PackageInstallObserver obs = new PackageInstallObserver();
            try {
                //(a)调用PKMS的installPackageWithVerification完成安装
                mPm.installPackageWithVerification(apkURI, obs, installFlags, installerPackageName,
                        verificationURI, null);
    
                synchronized (obs) {
                    while (!obs.finished) {
                        try {
                            obs.wait(); //等待安装结果
                        } catch (InterruptedException e) {
                        }
                    }
                    if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
                        System.out.println("Success");
                    } else {
                        System.err.println("Failure ["
                                + installFailureToString(obs.result)
                                + "]");//输出安装失败的原因
                    }
                }
            } catch (RemoteException e) {
                System.err.println(e.toString());
                System.err.println(PM_NOT_RUNNING_ERR);
            }
        }

    pm解析参数后,最终通过PKMS的Binder客户端调用installPackageWithVerification以完成后续的安装工作。

     以上写于2013-01-17 20:59

     未完待续。。

    先贴出来吧,有空看这块再补充,反思这半年.....

    continue my dream...
  • 相关阅读:
    docker安装kafka
    Prometheus警报
    MongoDB介绍
    SpringMvc中几个注解
    无DNS安装VCSA
    互联网本质
    什么是领导力
    58沈剑_一分钟专栏
    以数据库思维理解区块链
    区块链的4个实际应用
  • 原文地址:https://www.cnblogs.com/chenbin7/p/2865202.html
Copyright © 2011-2022 走看看