1. 程式人生 > >Android PackageManagerService分析一:PMS的啟動

Android PackageManagerService分析一:PMS的啟動

從這一章開始,我們來分析Android的PackageManagerService,後面簡稱PMS。PMS用來管理所有的package資訊,包括安裝、解除安裝、更新以及解析AndroidManifest.xml以組織相應的資料結構,這些資料結構將會被PMS、ActivityMangerService等等service和application使用到。PMS有幾個比較重要的命令可以用於我們debug中:

adb shell dumpsys package   (dump出系統中所有的application資訊)

adb shell dumpsys package “com.android.contacts" p  (dump出系統中特定包名的application資訊)

首先來看SystemServer中PMS的構造以及註冊:

            pm = PackageManagerService.main(context, installer,
                    factoryTest != SystemServer.FACTORY_TEST_OFF,
                    onlyCore);

            try {
                firstBoot = pm.isFirstBoot();
            } catch (RemoteException e) {
            }

        try {
            pm.performBootDexOpt();
        } catch (Throwable e) {
            reportWtf("performing boot dexopt", e);
        }

        try {
            pm.systemReady();
        } catch (Throwable e) {
            reportWtf("making Package Manager Service ready", e);
        }

首先來看PMS的main方法:
    public static final IPackageManager main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        ServiceManager.addService("package", m);
        return m;
    }

首先構造一個PMS物件,然後呼叫ServiceManager的addService註冊這個服務。建構函式的第二個引數是一個Installer物件,用於和Installd通訊使用,我們後面分析Installd再來介紹;第三個引數factoryTest為出廠測試,預設為false;第四個引數onlyCore與vold相關,我們以後再分析,這裡也為false。PMS的建構函式比較長,我們首先來看一下大概的流程圖,然後我們分段來分析程式碼:




    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {

        mContext = context;
        mFactoryTest = factoryTest;
        mOnlyCore = onlyCore;
        mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
        mMetrics = new DisplayMetrics();
        mSettings = new Settings(context);
        mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);

上面首先做一個變數的賦值,然後取出"ro.build.type"屬性值,build版本分為user和eng兩種,一種是面向user,一種是用於engineer debug版,這裡假設mNoDexOpt為false。然後構造一個Settings物件,Settings是Android的全域性管理者,用於協助PMS儲存所有的安裝包資訊,PMS和Settings之間的類圖關係如下:


來看一下Settings的建構函式:

    Settings(Context context) {
        this(context, Environment.getDataDirectory());
    }

    Settings(Context context, File dataDir) {
        mContext = context;
        mSystemDir = new File(dataDir, "system");
        mSystemDir.mkdirs();
        FileUtils.setPermissions(mSystemDir.toString(),
                FileUtils.S_IRWXU|FileUtils.S_IRWXG
                |FileUtils.S_IROTH|FileUtils.S_IXOTH,
                -1, -1);
        mSettingsFilename = new File(mSystemDir, "packages.xml");
        mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
        mPackageListFilename = new File(mSystemDir, "packages.list");
        FileUtils.setPermissions(mPackageListFilename, 0660, SYSTEM_UID, PACKAGE_INFO_GID);

        mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
        mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
    }

Environment.getDataDirectory()返回/data目錄,然後建立/data/system/目錄,並設定它的許可權,並在/data/system目錄中建立mSettingsFilename、mBackupSettingsFilename、mPackageListFilename、mStoppedPackagesFilename和mBackupStoppedPackagesFilename幾個檔案。packages.xml就是儲存了系統所有的Package資訊,packages-backup.xml是packages.xml的備份,防止在寫packages.xml突然斷電等問題。回到PMS的建構函式,呼叫addSharedUserLPw將幾種SharedUserId的名字和它對應的UID對應寫到Settings當中。關於SharedUserId的使用,我們在後面介紹APK的安裝過程中再來分析。這裡先簡單看一下Process中提供的UID列表:

    public static final int SYSTEM_UID = 1000;
    public static final int PHONE_UID = 1001;
    public static final int SHELL_UID = 2000;
    public static final int LOG_UID = 1007;
    public static final int WIFI_UID = 1010;
    public static final int MEDIA_UID = 1013;
    public static final int DRM_UID = 1019;
    public static final int VPN_UID = 1016;
    public static final int NFC_UID = 1027;
    public static final int BLUETOOTH_UID = 1002;
    public static final int MEDIA_RW_GID = 1023;
    public static final int PACKAGE_INFO_GID = 1032;
    public static final int FIRST_APPLICATION_UID = 10000;
    public static final int LAST_APPLICATION_UID = 19999;

上面定義了一系列的UID,其中applicantion的uid從10000開始到19999結束。來看addSharedUserLPw函式的實現:
    SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {
        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;
        }
        s = new SharedUserSetting(name, pkgFlags);
        s.userId = uid;
        if (addUserIdLPw(uid, s, name)) {
            mSharedUsers.put(name, s);
            return s;
        }
        return null;
    }

    private boolean addUserIdLPw(int uid, Object obj, Object name) {
        if (uid > Process.LAST_APPLICATION_UID) {
            return false;
        }

        if (uid >= Process.FIRST_APPLICATION_UID) {
            int N = mUserIds.size();
            final int index = uid - Process.FIRST_APPLICATION_UID;
            while (index >= N) {
                mUserIds.add(null);
                N++;
            }
            if (mUserIds.get(index) != null) {
                PackageManagerService.reportSettingsProblem(Log.ERROR,
                        "Adding duplicate user id: " + uid
                        + " name=" + name);
                return false;
            }
            mUserIds.set(index, obj);
        } else {
            if (mOtherUserIds.get(uid) != null) {
                PackageManagerService.reportSettingsProblem(Log.ERROR,
                        "Adding duplicate shared id: " + uid
                        + " name=" + name);
                return false;
            }
            mOtherUserIds.put(uid, obj);
        }
        return true;
    }


mSharedUsers是一個HashMap,儲存著所有的name和SharedUserSetting的對映關係。這裡先呼叫addUserIdLPw將uid和SharedUserSetting新增到mOtherUserIds中,然後將name和SharedUserSetting新增到mSharedUsers中方便以後查詢。接著來看PMS的建構函式:
        mInstaller = installer;

        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());
            Watchdog.getInstance().addThread(mHandler, mHandlerThread.getName(),
                    WATCHDOG_TIMEOUT);

            File dataDir = Environment.getDataDirectory();
            mAppDataDir = new File(dataDir, "data");
            mAppInstallDir = new File(dataDir, "app");
            mAppLibInstallDir = new File(dataDir, "app-lib");
            mAsecInternalPath = new File(dataDir, "app-asec").getPath();
            mUserAppDataDir = new File(dataDir, "user");
            mDrmAppPrivateInstallDir = new File(dataDir, "app-private");

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

            readPermissions();

上面首先獲得顯示屏的相關資訊並儲存在mMetrics中。然後啟動“PackageManager”的HandleThread並繫結到PackageHandler上,這就是最後處理所有的跨程序訊息的handler。接著呼叫readPermissions()來處理系統的permissions相關的檔案。在/etc/permissions的檔案大多來源於程式碼中的framworks/native/data/etc,這些檔案的作用是表明系統支援的feature有哪些,例如是否支援藍芽、wifi、P2P等。檔案目錄如下:


這裡的檔案內容很簡單,例如android.hardware.bluetooth.xml的內容如下:
<permissions>
    <feature name="android.hardware.bluetooth" />
</permissions>

在/etc/permissions中有一個platform.xml,它是來源於frameworks/base/data/etc/中,其中的內容大致如下:
<permissions>

    <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.BLUETOOTH_STACK" >
        <group gid="net_bt_stack" />
    </permission>

    <permission name="android.permission.NET_TUNNELING" >
        <group gid="vpn" />
    </permission>

    <permission name="android.permission.INTERNET" >
        <group gid="inet" />
    </permission>

    <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
    <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
    <assign-permission name="android.permission.WAKE_LOCK" uid="media" />
    <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="media" />
    <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="media" />

    <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />


    <library name="android.test.runner"
            file="/system/framework/android.test.runner.jar" />
    <library name="javax.obex"
            file="/system/framework/javax.obex.jar"/>

現在來看readPermissions()的實現:
    void readPermissions() {
        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;
        }

        for (File f : libraryDir.listFiles()) {
            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(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");
        readPermissionsFromXml(permFile);
    }

首先不斷的讀出/etc/permissions下面的檔案,並依此處理除了platform.xml以外的其它xml檔案,並最後處理platform.xml檔案,來看readPermissionsFromXml()的實現,這個函式比較長,我們主要看處理feature、permission、assign-permission和library的程式碼:
private void readPermissionsFromXml(File permFile) {
        FileReader permReader = null;
        try {
            permReader = new FileReader(permFile);
        } catch (FileNotFoundException e) {
        }

        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();
                if ("group".equals(name)) {

                } 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(parser, perm);

                } else if ("assign-permission".equals(name)) {
                    String perm = parser.getAttributeValue(null, "name");
                    String uidStr = parser.getAttributeValue(null, "uid");
                    if (uidStr == null) {
                        Slog.w(TAG, "<assign-permission> without uid at "
                                + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    int uid = Process.getUidForName(uidStr);
                    perm = perm.intern();
                    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)) {
                    String lname = parser.getAttributeValue(null, "name");
                    String lfile = parser.getAttributeValue(null, "file");
                    if (lname == null) {

                    } else if (lfile == null) {

                    } else {
                        mSharedLibraries.put(lname, new SharedLibraryEntry(lfile, null));
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;

                } else if ("feature".equals(name)) {
                    String fname = parser.getAttributeValue(null, "name");
                    if (fname == null) {

                    } else {
                        FeatureInfo fi = new FeatureInfo();
                        fi.name = fname;
                        mAvailableFeatures.put(fname, fi);
                    }
                    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);
        }
    }

首先來看處理feature這個tag的程式碼,在fname中儲存feature的名字,然後建立一個FeatureInfo,並把fname和FeatureInfo儲存到mAvailableFeatures這個HashMap中。接著來看處理permission tag,首先讀出permission的name,然後呼叫readPermission去處理後面的group資訊:
    void readPermission(XmlPullParser parser, String name)
            throws IOException, XmlPullParserException {

        name = name.intern();

        BasePermission bp = mSettings.mPermissions.get(name);
        if (bp == null) {
            bp = new BasePermission(name, null, BasePermission.TYPE_BUILTIN);
            mSettings.mPermissions.put(name, bp);
        }
        int outerDepth = parser.getDepth();
        int type;
        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
               && (type != XmlPullParser.END_TAG
                       || parser.getDepth() > outerDepth)) {
            if (type == XmlPullParser.END_TAG
                    || type == XmlPullParser.TEXT) {
                continue;
            }

            String tagName = parser.getName();
            if ("group".equals(tagName)) {
                String gidStr = parser.getAttributeValue(null, "gid");
                if (gidStr != null) {
                    int gid = Process.getGidForName(gidStr);
                    bp.gids = appendInt(bp.gids, gid);
                } else {
                    Slog.w(TAG, "<group> without gid at "
                            + parser.getPositionDescription());
                }
            }
            XmlUtils.skipCurrentTag(parser);
        }
    }

在readPermission中首先構造BasePermission物件,並把name和BasePermission一起新增到Settings的mPermissions這個HashMap中。Android管理許可權的機制其實就是對應相應的permission,用一個gid號來描述,當一個應用程式請求這個permission的時候,就把這個gid號新增到對應的application中去。Process.getGidForName方法通過JNI呼叫getgrnam系統函式去獲取相應的組名稱所對應的gid號,並把它新增到BasePermission物件的gids陣列中。再來看處理assign-permission這個tag的程式碼,首先讀出permission的名字和uid,儲存在perm和uidStr中,Process.getUidForName方法通過JNI呼叫getpwnam系統函式獲取相應的使用者名稱所對應的uid號,並把剛解析的permission名新增到HashSet當中,最後把上面的uid和hashset新增到mSystemPermissions這個陣列中。最後來看處理library這個tag的程式碼,這裡把解析處理的library名字和路徑儲存在mSharedLibraries這個hashMap中。再回到PMS的建構函式中,接著往下來看:
            mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
                    mSdkVersion, mOnlyCore);

            String customResolverActivity = Resources.getSystem().getString(
                    R.string.config_customResolverActivity);
            if (TextUtils.isEmpty(customResolverActivity)) {
                customResolverActivity = null;
            } else {
                mCustomResolverComponentName = ComponentName.unflattenFromString(
                        customResolverActivity);
            }

            long startTime = SystemClock.uptimeMillis();

            int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING;
            if (mNoDexOpt) {
                Slog.w(TAG, "Running ENG build: no pre-dexopt!");
                scanMode |= SCAN_NO_DEX;
            }

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

            String bootClassPath = System.getProperty("java.boot.class.path");
            if (bootClassPath != null) {
                String[] paths = splitString(bootClassPath, ':');
                for (int i=0; i<paths.length; i++) {
                    alreadyDexOpted.add(paths[i]);
                }
            } else {
                Slog.w(TAG, "No BOOTCLASSPATH found!");
            }

            boolean didDexOpt = false;

            if (mSharedLibraries.size() > 0) {
                Iterator<SharedLibraryEntry> libs = mSharedLibraries.values().iterator();
                while (libs.hasNext()) {
                    String lib = libs.next().path;
                    if (lib == null) {
                        continue;
                    }
                    try {
                        if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
                            alreadyDexOpted.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());
                    }
                }
            }

這裡首先呼叫Settings的readLPw函式去解析packages.xml和packages-backup.xml儲存的安裝列表資訊,並把解析的pakcages資訊新增到相應的資料結構中,這裡我們先假設這是第一次開機,所有packages.xml和packages-backup.xml檔案都還不存在。所以Settings的readLPw函式會直接返回。接著把boot class path裡面的檔案新增到alreadyDexOpted這個HashSet中,因為它們在zygote啟動時已經進過Dex優化了。接著掃描mSharedLibraries中的檔案,這些檔案是在解析platfrom.xml中的library tag新增進來的,如果它們需要做dex優化,則呼叫Installd的的dexopt方法,關於installd的呼叫流程,我們後面在安裝apk的時候再來分析。接著來看PMS的建構函式:

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

            alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");

            String[] frameworkFiles = frameworkDir.list();
            if (frameworkFiles != null) {
                for (int i=0; i<frameworkFiles.length; i++) {
                    File libPath = new File(frameworkDir, frameworkFiles[i]);
                    String path = libPath.getPath();
                    if (alreadyDexOpted.contains(path)) {
                        continue;
                    }
                    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);
                    }
                }
            }

            if (didDexOpt) {
                File dalvikCacheDir = new File(dataDir, "dalvik-cache");

                String[] files = dalvikCacheDir.list();
                if (files != null) {
                    for (int i=0; i<files.length; i++) {
                        String fn = files[i];
                        if (fn.startsWith("[email protected]@")
                                || fn.startsWith("[email protected]@")) {
                            Slog.i(TAG, "Pruning dalvik file: " + fn);
                            (new File(dalvikCacheDir, fn)).delete();
                        }
                    }
                }
            }

這裡掃描所有的/system/framework下面除framework-res以外的apk和jar包(因為framework-res只有resource檔案),然後依次對它們做Dex優化。在上面如果有對檔案做過Dex優化,就要去刪除dalvi-cache下面所有的dex檔案,以防止cache檔案和現在的檔案不相符。接著來看PMS的建構函式:
            mFrameworkInstallObserver = new AppDirObserver(
                frameworkDir.getPath(), OBSERVER_EVENTS, true, false);
            mFrameworkInstallObserver.startWatching();
            scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR
                    | PackageParser.PARSE_IS_PRIVILEGED,
                    scanMode | SCAN_NO_DEX, 0);

            // Collected privileged system packages.
            File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
            mPrivilegedInstallObserver = new AppDirObserver(
                    privilegedAppDir.getPath(), OBSERVER_EVENTS, true, true);
            mPrivilegedInstallObserver.startWatching();
                scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
                        | PackageParser.PARSE_IS_SYSTEM_DIR
                        | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0);

            // Collect ordinary system packages.
            File systemAppDir = new File(Environment.getRootDirectory(), "app");
            mSystemInstallObserver = new AppDirObserver(
                systemAppDir.getPath(), OBSERVER_EVENTS, true, false);
            mSystemInstallObserver.startWatching();
            scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);

            // Collect all vendor packages.
            File vendorAppDir = new File("/vendor/app");
            mVendorInstallObserver = new AppDirObserver(
                vendorAppDir.getPath(), OBSERVER_EVENTS, true, false);
            mVendorInstallObserver.startWatching();
            scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);

            if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
            mInstaller.moveFiles();

這裡首先會/system/framework、/system/priv-app、/system/app、/vendor/app四個目錄建立AppDirObserver去監聽它們的add、delete等操作,AppDirObserver是繼承於FileObserver,它的底層是通過linux核心的inotify機制實現的。接著呼叫scanDirLI去掃描上面的四個目錄。我們來看一下AppDirObserver的架構:


接著來看scanDirLI的程式碼:

    private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
        String[] files = dir.list();

        int i;
        for (i=0; i<files.length; i++) {
            File file = new File(dir, files[i]);
            if (!isPackageFilename(files[i])) {

                continue;
            }
            PackageParser.Package pkg = scanPackageLI(file,
                    flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);

            if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                    mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {

                Slog.w(TAG, "Cleaning up failed install of " + file);
                file.delete();
            }
        }
    }

scanDirLI呼叫scanPackageLI依次掃描並解析上面四個目錄的目錄下所有的apk檔案:
    private PackageParser.Package scanPackageLI(File scanFile,
            int parseFlags, int scanMode, long currentTime, UserHandle user) {
        mLastScanError = PackageManager.INSTALL_SUCCEEDED;
        String scanPath = scanFile.getPath();

        parseFlags |= mDefParseFlags;
        PackageParser pp = new PackageParser(scanPath);

        //首先解析出一個Package物件
        final PackageParser.Package pkg = pp.parsePackage(scanFile,
                scanPath, mMetrics, parseFlags);

        PackageSetting ps = null;
        PackageSetting updatedPkg;

        synchronized (mPackages) {

            String oldName = mSettings.mRenamedPackages.get(pkg.packageName);
            if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {

                ps = mSettings.peekPackageLPr(oldName);
            }
            if (ps == null) {
                ps = mSettings.peekPackageLPr(pkg.packageName);
            }

            updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
            if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);
        }

        if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
            //與update app相關的
        }

        if (updatedPkg != null) {
            parseFlags |= PackageParser.PARSE_IS_SYSTEM;
        }

        if (!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)) {
            Slog.w(TAG, "Failed verifying certificates for package:" + pkg.packageName);
            return null;
        }

        //處理system與非system的app同名的問題
        boolean shouldHideSystemApp = false;
        if (updatedPkg == null && ps != null
                && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {

            if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
                    != PackageManager.SIGNATURE_MATCH) {
                if (DEBUG_INSTALL) Slog.d(TAG, "Signature mismatch!");
                deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false);
                ps = null;
            } else {

                if (pkg.mVersionCode < ps.versionCode) {
                    shouldHideSystemApp = true;
                } else {
                    InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),
                            ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString);
                    synchronized (mInstallLock) {
                        args.cleanUpResourcesLI();
                    }
                }
            }
        }

        if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
            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 {

            }
        } else {
            resPath = pkg.mScanPath;
        }

        codePath = pkg.mScanPath;
        setApplicationInfoPaths(pkg, codePath, resPath);
        
        //
        PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
                | SCAN_UPDATE_SIGNATURE, currentTime, user);
                
        if (shouldHideSystemApp) {
            synchronized (mPackages) {
                grantPermissionsLPw(pkg, true);
                mSettings.disableSystemPackageLPw(pkg.packageName);
            }
        }

        return scannedPkg;
    }

scanPackageLI首先呼叫PackageParser的parsePackage去解析掃描的檔案,注意這裡有兩個parsePackage函式,但它們的引數不同,我們來看以File為第一個引數的parsePackage方法:

    public Package parsePackage(File sourceFile, String destCodePath,
            DisplayMetrics metrics, int flags) {
        mParseError = PackageManager.INSTALL_SUCCEEDED;

        mArchiveSourcePath = sourceFile.getPath();

        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);
                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 {
            // XXXX todo: need to figure out correct configuration.
            pkg = parsePackage(res, parser, flags, errorText);
        } catch (Exception e) {
            errorException = e;
            mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
        }

        parser.close();
        assmgr.close();

        pkg.mPath = destCodePath;
        pkg.mScanPath = mArchiveSourcePath;
        pkg.mSignatures = null;

        return pkg;
    }

首先從apk檔案中開啟AndroidManifest.xml檔案,然後呼叫以Resources為第一個引數的parsePackage方法,這個函式比較長,主要就是解析AndroidManifest.xml檔案,建立一個Package物件,大概類圖如下。最後設定Package物件的mPath和mScanPath為當前APK所在的全路徑名。


我們以Mms這個應用的Manifest檔案來看分析解析後的結果,首先來看Mms的AndroidManifest.xml檔案(這裡只截取了一部分)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.android.mms">
    <original-package android:name="com.android.mms" />
    <uses-permission android:name="android.permission.RECEIVE_MMS" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <!-- System apps can access the  receiver through intent-->
    <permission android:name="android.permission.MMS_SEND_OUTBOX_MSG"
                android:protectionLevel="signatureOrSystem"
                android:label="@string/label_mms_send_outbox_msg"
                android:description="@string/desc_mms_send_outbox_msg"/>
    <application android:name="MmsApp"
            android:label="@string/app_label"
            android:icon="@mipmap/ic_launcher_smsmms"
            android:taskAffinity="android.task.mms"
            android:allowTaskReparenting="true">
        <activity android:name=".ui.ConversationList"
                  android:label="@string/app_label"
                  android:configChanges="orientation|screenSize|keyboardHidden"
                  android:theme="@style/MmsHoloTheme"
                  android:uiOptions="splitActionBarWhenNarrow"
                  android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.APP_MESSAGING" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="vnd.android.cursor.dir/mms" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="vnd.android-dir/mms-sms" />
            </intent-filter>
        </activity>
        <activity android:name=".ui.ComposeMessageActivity"
                  android:configChanges="orientation|screenSize|keyboardHidden"
                  android:windowSoftInputMode="stateHidden|adjustResize"
                  android:theme="@style/MmsHoloTheme"
                  android:parentActivityName=".ui.ConversationList"
                  android:launchMode="singleTop" >
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="vnd.android-dir/mms-sms" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.SENDTO" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="sms" />
                <data android:scheme="smsto" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.SENDTO" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="mms" />
                <data android:scheme="mmsto" />
            </intent-filter>
           <intent-filter>
               <action android:name="android.intent.action.SEND" />
               <category android:name="android.intent.category.DEFAULT" />
               <data android:mimeType="image/*" />
           </intent-filter>
           <intent-filter>
               <action android:name="android.intent.action.SEND" />
               <category android:name="android.intent.category.DEFAULT" />
               <data android:mimeType="text/plain" />
           </intent-filter>
           <intent-filter>
               <action android:name="android.intent.action.SEND_MULTIPLE" />
               <category android:name="android.intent.category.DEFAULT" />
               <data android:mimeType="image/*" />
           </intent-filter>
        </activity>
        <receiver android:name=".transaction.PushReceiver"
            android:permission="android.permission.BROADCAST_WAP_PUSH">
            <intent-filter>
                <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
                <data android:mimeType="application/vnd.wap.mms-message" />
            </intent-filter>
        </receiver>
        <receiver android:name=".transaction.SmsReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
            <intent-filter>
                <action android:name="com.android.mms.transaction.MESSAGE_SENT" />
                <!-- TODO Do a better data match here. -->
                <data android:scheme="content" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEND_MESSAGE" />
            </intent-filter>
        </receiver>
        <provider android:name="SuggestionsProvider"
            android:exported="true"
            android:readPermission="android.permission.READ_SMS"
            android:authorities="com.android.mms.SuggestionsProvider" >
            <path-permission
                    android:pathPrefix="/search_suggest_query"
                    android:readPermission="android.permission.GLOBAL_SEARCH" />
            <path-permission
                    android:pathPrefix="/search_suggest_shortcut"
                    android:readPermission="android.permission.GLOBAL_SEARCH" />
        </provider>
        <service android:name=".ui.NoConfirmationSendService"
                 android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
                 android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="sms" />
                <data android:scheme="smsto" />
            </intent-filter>
        </service>
    </application>
</manifest>

上面的AndroidManifest.xml檔案中定義了2個Activity,2個receiver,1個service和1個provider,我們來看進過parsePackage得到的Package物件如下:



接著回到scanPackageLI方法,解析完AndroidManifest.xml檔案後,再來檢查是否是更新的APK,如果更新的APK版本比以前的版本還有低,則直接返回;如果更新的APK版本比以前的版本高,則去刪除之前的APK以及resource檔案。若不是更新APK,並且當前package是系統app,但之前安裝了非系統的app,這裡首先比較簽名,如果簽名不一致,則直接刪除當前package;若簽名檔案一致,則首先比較當前package和之前的版本號,如果當前版本號比較新,則直接刪除之前的APK以及resource檔案。最後呼叫scanPackageLI方法讓把當前package的資訊歸入到PMS中的資料結構:

    private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
            int parseFlags, int scanMode, long currentTime, UserHandle user) {
        File scanFile = new File(pkg.mScanPath);
        mScanningPath = scanFile;

        if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        }

        if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_PRIVILEGED;
        }

        if (mCustomResolverComponentName != null &&
                mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
            setUpCustomResolverActivity(pkg);
        }

        if (pkg.packageName.equals("android")) {
            synchronized (mPackages) {
                if (mAndroidApplication != null) {
                    mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
                    return null;
                }

                mPlatformPackage = pkg;
                pkg.mVersionCode = mSdkVersion;
                mAndroidApplication = pkg.applicationInfo;

                if (!mResolverReplaced) {
                    mResolveActivity.applicationInfo = mAndroidApplication;
                    mResolveActivity.name = ResolverActivity.class.getName();
                    mResolveActivity.packageName = mAndroidApplication.packageName;
                    mResolveActivity.processName = "system:ui";
                    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;
                    mResolveInfo.priority = 0;
                    mResolveInfo.preferredOrder = 0;
                    mResolveInfo.match = 0;
                    mResolveComponentName = new ComponentName(
                            mAndroidApplication.packageName, mResolveActivity.name);
                }
            }
        }

        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) {
            if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                if (!updateSharedLibrariesLPw(pkg, null)) {
                    return null;
                }
            }

            if (pkg.mSharedUserId != null) {
                suid = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, 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);
                }
            }
            

這裡的mCustomResolverComponentName預設是空,採用framework是本身的ResolverActivity去解析intent。mAndroidApplication在Android系統中只有一個這樣的application,就是framework-res.apk,它的packageName是"android"。然後在mResolveActivity和mResolveInfo儲存ResolverActivity的資訊,ResolverActivity用於在啟動Activity的時候,如果有多個activity符合條件,彈出對話方塊給使用者選擇,這部分我們在以後分析AcitivityManagerService的時候再來分析。如果在Manifest中指定了ShareUserId,則首先獲取一個關聯的SharedUserSetting物件:

    SharedUserSetting getSharedUserLPw(String name,
            int pkgFlags, boolean create) {
        SharedUserSetting s = mSharedUsers.get(name);
        if (s == null) {
            if (!create) {
                return null;
            }
            s = new SharedUserSetting(name, pkgFlags);
            s.userId = newUserIdLPw(s);
            Log.i(PackageManagerService.TAG, "New shared user " + name + ": id=" + s.userId);
            // < 0 means we couldn't assign a userid; fall out and return
            // s, which is currently null
            if (s.userId >= 0) {
                mSharedUsers.put(name, s);
            }
        }

        return s;
    }

在開始PMS的建構函式裡面我們知道,系統會首先新增一系列的sysem的user id到mSharedUsers,所以如果能夠從mSharedUsers獲得到就直接返回;如果不能,則首先構造一個SharedUserSetting,並指派一個沒有使用的APPLICATION UID,當然APPLICATION UID的值是在FIRST_APPLICATION_UID到LAST_APPLICATION_UID之間。最後把建立的SharedUserSetting新增到mSharedUsers和mUserIds陣列當中。接著來看scanPackageLI函式:
            PackageSetting origPackage = null;
            String realName = null;
            if (pkg.mOriginalPackages != null) {
             //關於應用命名和更新的程式碼
            }
            
            pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
                    destResourceFile, pkg.applicationInfo.nativeLibraryDir,
                    pkg.applicationInfo.flags, user, 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) {
                pkg.setPackageName(origPackage.name);
                
                String msg = "New package " + pkgSetting.realName
                        + " renamed to replace old package " + pkgSetting.name;
                reportSettingsProblem(Log.WARN, msg);
                
                mTransferedPackages.add(origPackage.name);
                
                pkgSetting.origPackage = null;
            }
            
            if (realName != null) {
                mTransferedPackages.add(pkg.packageName);
            }
            
            if (mSettings.isDisabledSystemPackageLPr(pkg.packageName)) {
                pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
            }

            if (mFoundPolicyFile) {
                SELinuxMMAC.assignSeinfoValue(pkg);
            }

            pkg.applicationInfo.uid = pkgSetting.appId;
            pkg.mExtras = pkgSetting;

            if (!verifySignaturesLP(pkgSetting, pkg)) {
                if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                    return null;
                }
                pkgSetting.signatures.mSignatures = pkg.mSignatures;
                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;
                    }
                }
                String msg = "System package " + pkg.packageName
                        + " signature changed; retaining data.";
                reportSettingsProblem(Log.WARN, msg);
            }

關於應用程式改名和更新的程式碼我們這裡先忽略,首先來看構造PackageSetting的方法:

    PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,
            String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
            String nativeLibraryPathString, int pkgFlags, UserHandle user, boolean add) {
        final String name = pkg.packageName;
        PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath,
                resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags,
                user, add, true /* allowInstall */);
        return p;
    }

    private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
            String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
            String nativeLibraryPathString, int vc, int pkgFlags,
            UserHandle installUser, boolean add, boolean allowInstall) {
        PackageSetting p = mPackages.get(name);
        if (p != null) {
          //更新apk相關
        }
        if (p == null) {
            if (origPackage != null) {
                 //更新apk相關
            } else {
                p = new PackageSetting(name, realName, codePath, resourcePath,
                        nativeLibraryPathString, vc, pkgFlags);
                p.setTimeStamp(codePath.lastModified());
                p.sharedUser = sharedUser;
                if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
                    List<UserInfo> users = getAllUsers();
                    if (users != null && allowInstall) {
                         //多使用者的部分
                        }
                    }
                }
                if (sharedUser != null) {
                    p.appId = sharedUser.userId;
                } else {
                   //更新系統apk相關
            }

            if (add) {
                // Finish adding new package by adding it and updating shared
                // user preferences
                addPackageSettingLPw(p, name, sharedUser);
            }
        } else {
            //多使用者的部分
        }
        return p;
    }

關於PackageSetting結構可以看文章最開始的類圖。在新建一個PackageSetting物件後,首先將得到的系統的uid值賦給applicationInfo.uid ,這就是當前APK以後執行時的UID了。然後就做數字簽名驗證,這裡主要是對於更新APK來做驗證。在做完數字簽名驗證後,還需要檢查當前APK是否提供providers與系統已有的providers衝突,如果衝突,則提示安裝失敗。接著來看scanPackageLI函式:
        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.
            final Iterator<PackageCleanItem> iter = mSettings.mPackagesToBeCleaned.iterator();
            while (iter.hasNext()) {
                PackageCleanItem item = iter.next();
                if (pkgName.equals(item.packageName)) {
                    iter.remove();
                }
            }

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

            // Add the package's KeySets to the global KeySetManager
            KeySetManager ksm = mSettings.mKeySetManager;
            try {
                ksm.addSigningKeySetToPackage(pkg.packageName, pkg.mSigningKeys);
                if (pkg.mKeySetMapping != null) {
                    for (Map.Entry<String, Set<PublicKey>> entry : pkg.mKeySetMapping.entrySet()) {
                        if (entry.getValue() != null) {
                            ksm.addDefinedKeySetToPackage(pkg.packageName,
                                entry.getValue(), entry.getKey());
                        }
                    }
                }
            } catch (NullPointerException e) {
                Slog.e(TAG, "Could not add KeySet to " + pkg.packageName, e);
            } catch (IllegalArgumentException e) {
                Slog.e(TAG, "Could not add KeySet to malformed package" + pkg.packageName, e);
            }

首先呼叫Settings的insertPackageSettingLPw將pkgSetting物件加入到Settings中的mPackages這個HashMap中。在insertPackageSettingLPw方法中,首先將Package中的一些資訊賦予給PackageSetting,然後呼叫addPackageSettingLPw方法將PackageSetting物件新增到mPackages中,並將PackageSetting加入到SharedUserSetting中的packages這個HashSet中。接著將pkg物件加入到PMS的mPackages這個HashMap中,儲存在mPackages中資訊會被後面很多地方使用到。最後對apk的安裝或者更新時間做相應的更新。接著來看scanPackageLI函式:
            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);
                mProviders.addProvider(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) {
                            p = new PackageParser.Provider(p);
                            p.syncable = false;
                        }
                        if (!mProvidersByAuthority.containsKey(names[j])) {
                            mProvidersByAuthority.put(names[j], p);
                            if (p.info.authority == null) {
                                p.info.authority = names[j];
                            } else {
                                p.info.authority = p.info.authority + ";" + names[j];
                            }
                        } else {
                            PackageParser.Provider other = mProvidersByAuthority.get(names[j]);
                        }
                    }
                }
                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);
            }

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

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

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