PackageManagerService學(xué)習(xí)--上

  • 原文發(fā)布與個(gè)人博客KuTear,轉(zhuǎn)載請(qǐng)注明*

pm命令的使用

pm 基本使用

pm工具在Android系統(tǒng)System/bin下,由于該路徑已經(jīng)在環(huán)境變量PATH下,所以我們可以在Android終端下運(yùn)行pm命令

pm <command>

通過鍵入pm我們可以看見他的用法,主要包括以下幾個(gè)方面

  • list => 列出安裝的APK/權(quán)限組/機(jī)器硬件功能(NFC/藍(lán)牙/WIFI)/設(shè)備支持庫(kù)
  • path => 查詢指定apk的路徑
  • install => APK安裝
  • dump => 獲取系統(tǒng)中的信息(電量/activity/server)
  • clear => 清除APK數(shù)據(jù)
  • hide/unhide => 隱藏與恢復(fù)應(yīng)用(被隱藏應(yīng)用在應(yīng)用管理中變得不可見,桌面圖標(biāo)也會(huì)消失)
  • enable/disable => 禁用和啟用應(yīng)用
  • grant/revoke => 賦予/撤銷應(yīng)用權(quán)限

參考:初探Android的PMS服務(wù)

pm 命令源碼分析

Pm命令的源碼在framework/base/cmds/pm下(本文基于5.1.1 r1 代碼地址),只有一個(gè)類Pm.java,在其靜態(tài)入口函數(shù)main(args)中執(zhí)行函數(shù)run(args),這里我們可以看看其中一部分Pm list package

    public int run(String[] args) throws IOException, RemoteException {
        boolean validCommand = false;
        if (args.length < 1) {
            return showUsage();
        }

        mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
        if (mPm == null) {
            System.err.println(PM_NOT_RUNNING_ERR);
            return 1;
        }
        mInstaller = mPm.getPackageInstaller();

        mArgs = args;
        String op = args[0];
        mNextArg = 1;

        if ("list".equals(op)) {
            return runList();
        }
            //....其他命令
    }

上面的代碼通過進(jìn)程間通信獲取的PackageManagaerServer,然后運(yùn)行具體的指令,下面看看list指令是怎么獲取到數(shù)據(jù)的。

    /**
     * Execute the list sub-command.
     *
     * pm list [package | packages]
     * pm list permission-groups
     * pm list permissions
     * pm list features
     * pm list libraries
     * pm list instrumentation
     */
    private int runList() {
        String type = nextArg();
        if (type == null) {
            System.err.println("Error: didn't specify type of data to list");
            return 1;
        }
        if ("package".equals(type) || "packages".equals(type)) {
            return runListPackages(false);
        } else if ("permission-groups".equals(type)) {
            return runListPermissionGroups();
        } else if ("permissions".equals(type)) {
            return runListPermissions();
        } else if ("features".equals(type)) {
            return runListFeatures();
        } else if ("libraries".equals(type)) {
            return runListLibraries();
        } else if ("instrumentation".equals(type)) {
            return runListInstrumentation();
        } else if ("users".equals(type)) {
            return runListUsers();
        } else {
            System.err.println("Error: unknown list type '" + type + "'");
            return 1;
        }
    }

這里沒有實(shí)質(zhì)性的操作,只是分發(fā)list的子指令,下面我們看package指令的實(shí)現(xiàn)邏輯。

    /* 
     * Lists all the installed packages.
     */
    private int runListPackages(boolean showApplicationPackage) {
        int getFlags = 0;
        boolean listDisabled = false, listEnabled = false;
        boolean listSystem = false, listThirdParty = false;
        boolean listInstaller = false;
        //A user id constant to indicate the "owner" user of the device
        int userId = UserHandle.USER_OWNER;
        try {
            String opt;
            while ((opt=nextOption()) != null) {
                if (opt.equals("-l")) {
                    // old compat
                } else if (opt.equals("-lf")) {
                    showApplicationPackage = true;
                } else if (opt.equals("-f")) {
                    showApplicationPackage = true;
                } else if (opt.equals("-d")) {
                    listDisabled = true;
                } else if (opt.equals("-e")) {
                    listEnabled = true;
                } else if (opt.equals("-s")) {
                    listSystem = true;
                } else if (opt.equals("-3")) {
                    listThirdParty = true;
                } else if (opt.equals("-i")) {
                    listInstaller = true;
                } else if (opt.equals("--user")) {
                    userId = Integer.parseInt(nextArg());
                } else if (opt.equals("-u")) {
                    getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
                } else {
                    System.err.println("Error: Unknown option: " + opt);
                    return 1;
                }
            }
        } catch (RuntimeException ex) {
            System.err.println("Error: " + ex.toString());
            return 1;
        }

        String filter = nextArg();

        try {
            //重點(diǎn)關(guān)注
            final List<PackageInfo> packages = getInstalledPackages(mPm, getFlags, userId);

            int count = packages.size();
            for (int p = 0 ; p < count ; p++) {
                PackageInfo info = packages.get(p);
                if (filter != null && !info.packageName.contains(filter)) {
                    continue;
                }
                final boolean isSystem =
                        (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
                if ((!listDisabled || !info.applicationInfo.enabled) &&
                        (!listEnabled || info.applicationInfo.enabled) &&
                        (!listSystem || isSystem) &&
                        (!listThirdParty || !isSystem)) {
                    System.out.print("package:");
                    if (showApplicationPackage) {
                        System.out.print(info.applicationInfo.sourceDir);
                        System.out.print("=");
                    }
                    System.out.print(info.packageName);
                    if (listInstaller) {
                        System.out.print("  installer=");
                        System.out.print(mPm.getInstallerPackageName(info.packageName));
                    }
                    System.out.println();
                }
            }
            return 0;
        } catch (RemoteException e) {
            System.err.println(e.toString());
            System.err.println(PM_NOT_RUNNING_ERR);
            return 1;
        }
    }

上面的代碼一方面是添加過濾參數(shù),然后獲取所有安裝的APK,在根據(jù)過濾參數(shù)展示對(duì)應(yīng)的APK,而我們最為關(guān)心的就是獲取所有APK的過程.到底是怎么獲取的呢, 我們接著往下看。

    @SuppressWarnings("unchecked")
    private List<PackageInfo> getInstalledPackages(IPackageManager pm, int flags, int userId)
            throws RemoteException {
        ParceledListSlice<PackageInfo> slice = pm.getInstalledPackages(flags, userId);
        return slice.getList();
    }

到此,我們發(fā)現(xiàn)其實(shí)獲取APK列表的過程是由IPackageManager來實(shí)現(xiàn)的,同樣的方式分析其他命令的操作,其最后都是歸集到IPackageManager上,而IPackageManager就是我們今天的主角,下面我們會(huì)重點(diǎn)分析PM啟動(dòng)的過程

PackageManagerServer的啟動(dòng)位置

SystemServer啟動(dòng)后,會(huì)實(shí)例化PackageManagerServer, 具體位置在PackageManagerServer#startBootstrapServices() 。

mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);

在這個(gè)main()函數(shù)中實(shí)現(xiàn)很簡(jiǎn)單,只是實(shí)例化一個(gè)PMS對(duì)象,然后將其加入SystemServer中,而最為重要的過程就是PackageManagerServer的實(shí)例化的過程,下面將會(huì)重點(diǎn)分析這個(gè)過程。

PackageManagerServer 的實(shí)例過程

PM相關(guān)的目錄

  • 系統(tǒng)APK

    • /system/priv-app

    • /system/app

    • /vendor/app

    • /oem/app

  • 用戶APK

    • /data/app

PMS配置文件

  • /data/system/packages.xml

    記錄系統(tǒng)中所有已經(jīng)安裝的應(yīng)用信息,包括基本信息,簽名和權(quán)限。

  • /system/etc/permissions/

    讀取當(dāng)前Android設(shè)備的硬件特性和設(shè)定的相關(guān)權(quán)限。

參考:PMS運(yùn)行時(shí)的一些規(guī)則

PM初始化過程

//PackageManagerServer.java構(gòu)造函數(shù)
    //build類型,可以在shell下通過getprop來查看全部 
    //使用getprop ro.build.type獲取到該build type
    //build type有user,userdebug,eng
    //user:性能好,Log/Debug信息少,相當(dāng)于是正式版
    //eng:Log/Debug信息相對(duì)多
    //userdebug:debug強(qiáng)
    //根據(jù)這里的返回,我們猜測(cè)返回值和dexopt相關(guān)
    mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
    mMetrics = new DisplayMetrics();
    mSettings = new Settings(mPackages);
    mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

上面主要就是新建一個(gè)Setting對(duì)象,然后調(diào)用函數(shù)addSharedUserLPw(...),在Setting的構(gòu)造函數(shù)中主要就是為上文說的/data/system/packages.xml等文件的創(chuàng)建和賦權(quán)限.現(xiàn)在看看函數(shù)addSharedUserLPw()的實(shí)現(xiàn)。

   SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
        //ArrayMap<String, SharedUserSetting> mSharedUsers
        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, pkgPrivateFlags);
        s.userId = uid;
        if (addUserIdLPw(uid, s, name)) {
            mSharedUsers.put(name, s);
            return s;
        }
        return null;
    }

該函數(shù)主要就是共享UID,例如有的系統(tǒng)應(yīng)用會(huì)有

android:sharedUserId="android.uis.system"

而根據(jù)上面的設(shè)置就是該APK的UID為Process.SYSTEM_UID,從而達(dá)到共享系統(tǒng)UID的目的.而下面調(diào)用的函數(shù)addUserIdLPw(...)就是保存該UID和對(duì)應(yīng)的ShareUserSetting 。接著看PackageMangerService的構(gòu)造函數(shù)的下一部分。

    SystemConfig systemConfig = SystemConfig.getInstance();
    mGlobalGids = systemConfig.getGlobalGids();
    mSystemPermissions = systemConfig.getSystemPermissions();
    mAvailableFeatures = systemConfig.getAvailableFeatures();

SystemConfig在構(gòu)造函數(shù)里對(duì)一些目錄進(jìn)行讀取,這些目錄包括

/system/etc/sysconfig
/system/etc/permissions
/oem/etc/sysconfig
/oem/etc/permissions

然后解析這些目錄下面的文件,這些文件的作用是聲明當(dāng)前設(shè)備的功能(NFC/WIFI)等.下面我們看看解析函數(shù)

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

        final boolean lowRam = ActivityManager.isLowRamDeviceStatic();

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

            int type;
            while ((type=parser.next()) != parser.START_TAG
                       && type != parser.END_DOCUMENT) {
                ;
            }

            if (type != parser.START_TAG) {
                throw new XmlPullParserException("No start tag found");
            }
            //要求根結(jié)點(diǎn)為permissions OR config
            if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
                throw new XmlPullParserException("Unexpected start tag in " + permFile
                        + ": found " + parser.getName() + ", expected 'permissions' or 'config'");
            }
            //遍歷XML,并將不同的功能保存到不同的ArraySet<>中
            while (true) {
                XmlUtils.nextElement(parser);
                if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
                    break;
                }

                String name = parser.getName();
                if ("group".equals(name) && !onlyFeatures) {
                    String gidStr = parser.getAttributeValue(null, "gid");
                    if (gidStr != null) {
                        int gid = android.os.Process.getGidForName(gidStr);
                        mGlobalGids = appendInt(mGlobalGids, gid);
                    } else {
                        Slog.w(TAG, "<group> without gid in " + permFile + " at "
                                + parser.getPositionDescription());
                    }

                    XmlUtils.skipCurrentTag(parser);
                    continue;
                } else if ("permission".equals(name) && !onlyFeatures) {
                    String perm = parser.getAttributeValue(null, "name");
                    if (perm == null) {
                        Slog.w(TAG, "<permission> without name in " + permFile + " at "
                                + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    perm = perm.intern();
                    readPermission(parser, perm);

                } else if ("assign-permission".equals(name) && !onlyFeatures) {
                    String perm = parser.getAttributeValue(null, "name");
                    if (perm == null) {
                        Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "
                                + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    String uidStr = parser.getAttributeValue(null, "uid");
                    if (uidStr == null) {
                        Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "
                                + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    int uid = Process.getUidForName(uidStr);
                    if (uid < 0) {
                        Slog.w(TAG, "<assign-permission> with unknown uid \""
                                + uidStr + "  in " + permFile + " at "
                                + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    perm = perm.intern();
                    ArraySet<String> perms = mSystemPermissions.get(uid);
                    if (perms == null) {
                        perms = new ArraySet<String>();
                        mSystemPermissions.put(uid, perms);
                    }
                    perms.add(perm);
                    XmlUtils.skipCurrentTag(parser);

                } else if ("library".equals(name) && !onlyFeatures) {
                    String lname = parser.getAttributeValue(null, "name");
                    String lfile = parser.getAttributeValue(null, "file");
                    if (lname == null) {
                        Slog.w(TAG, "<library> without name in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else if (lfile == null) {
                        Slog.w(TAG, "<library> without file in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else {
                        //Log.i(TAG, "Got library " + lname + " in " + lfile);
                        mSharedLibraries.put(lname, lfile);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;

                } else if ("feature".equals(name)) {
                    String fname = parser.getAttributeValue(null, "name");
                    boolean allowed;
                    if (!lowRam) {
                        allowed = true;
                    } else {
                        String notLowRam = parser.getAttributeValue(null, "notLowRam");
                        allowed = !"true".equals(notLowRam);
                    }
                    if (fname == null) {
                        Slog.w(TAG, "<feature> without name in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else if (allowed) {
                        //Log.i(TAG, "Got feature " + fname);
                        FeatureInfo fi = new FeatureInfo();
                        fi.name = fname;
                        mAvailableFeatures.put(fname, fi);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;

                } else if ("unavailable-feature".equals(name)) {
                    String fname = parser.getAttributeValue(null, "name");
                    if (fname == null) {
                        Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else {
                        mUnavailableFeatures.add(fname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;

                } else if ("allow-in-power-save-except-idle".equals(name) && !onlyFeatures) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, "<allow-in-power-save-except-idle> without package in "
                                + permFile + " at " + parser.getPositionDescription());
                    } else {
                        mAllowInPowerSaveExceptIdle.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;

                } else if ("allow-in-power-save".equals(name) && !onlyFeatures) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else {
                        mAllowInPowerSave.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;

                } else if ("fixed-ime-app".equals(name) && !onlyFeatures) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, "<fixed-ime-app> without package in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else {
                        mFixedImeApps.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;

                } else if ("app-link".equals(name)) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, "<app-link> without package in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else {
                        mLinkedApps.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);

                } else {
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                }
            }
        } catch (XmlPullParserException e) {
            Slog.w(TAG, "Got exception parsing permissions.", e);
        } catch (IOException e) {
            Slog.w(TAG, "Got exception parsing permissions.", e);
        } finally {
            IoUtils.closeQuietly(permReader);
        }

        for (String fname : mUnavailableFeatures) {
            if (mAvailableFeatures.remove(fname) != null) {
                Slog.d(TAG, "Removed unavailable feature " + fname);
            }
        }
    }

上面代碼很容易理解,解析xml節(jié)點(diǎn)的數(shù)據(jù)到具體的List或Set中,在PackageManagerService的構(gòu)造函數(shù)中就取出了mGlobalGids/mAvailableFeatures/mSystemPermissions出來,分別對(duì)應(yīng)的TAG節(jié)點(diǎn)為

<group gid="" ></group>
<feature></feature>
<assign-permission></assign-permission>

下面接著分析PackageManagerService的構(gòu)造函數(shù)

        mHandlerThread = new ServiceThread(TAG,
                Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);  //HandlerThread
        mHandlerThread.start();
        mHandler = new PackageHandler(mHandlerThread.getLooper());
        Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);

        File dataDir = Environment.getDataDirectory(); //dataDir = "/data/"
        mAppDataDir = new File(dataDir, "data");       //  = "/data/data"
        mAppInstallDir = new File(dataDir, "app");     //  = "/data/app"
        mAppLib32InstallDir = new File(dataDir, "app-lib"); // = "/data/app-lib"
        mAsecInternalPath = new File(dataDir, "app-asec").getPath();  // = "data/app-asec"
        mUserAppDataDir = new File(dataDir, "user");   //=  "/data/user"
        mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); // = "/data/app-private"
        sUserManager = new UserManagerService(context, this,
                mInstallLock, mPackages);

        // Propagate permission configuration in to package manager.
        //合并SystemConfig讀取的permission到Settings下
        ArrayMap<String, SystemConfig.PermissionEntry> permConfig
                = systemConfig.getPermissions(); //permission標(biāo)簽下的
        for (int i=0; i<permConfig.size(); i++) {
            SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
            BasePermission bp = mSettings.mPermissions.get(perm.name);
            if (bp == null) {
                bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
                mSettings.mPermissions.put(perm.name, bp);
            }
            if (perm.gids != null) {
                bp.setGids(perm.gids, perm.perUser);
            }
        }
        //從SystemConfig中讀取的libs存入共享庫(kù)
        ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
        for (int i=0; i<libConfig.size(); i++) {
            mSharedLibraries.put(libConfig.keyAt(i),
                    new SharedLibraryEntry(libConfig.valueAt(i), null));
        }

        mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();

上面的代碼主要就是通過上面的SystemConfig獲取到的信息存在在SettingsPMS內(nèi)部。

       //這里會(huì)讀取前面說的/data/system/packages.xml文件以及他的備份文件
       //這里特別說明下,會(huì)把解析的application存放到mSetting的mPackages中,后面會(huì)用到
        mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
                mSdkVersion, mOnlyCore);

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

        long startTime = SystemClock.uptimeMillis();

        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                startTime);

        // Set flag to monitor and not change apk file paths when
        // scanning install directories.
        final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;
        //已經(jīng)dexopt的apk存放位置
        final ArraySet<String> alreadyDexOpted = new ArraySet<String>();

        /**
         * Add everything in the in the boot class path to the
         * list of process files because dexopt will have been run
         * if necessary during zygote startup.
         */
        final String bootClassPath = System.getenv("BOOTCLASSPATH");
        final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
        //系統(tǒng)庫(kù)類不要優(yōu)化  可以通過echo $BOOTCLASSPATH查看
        if (bootClassPath != null) {
            String[] bootClassPathElements = splitString(bootClassPath, ':');
            for (String element : bootClassPathElements) {
                alreadyDexOpted.add(element);
            }
        } else {
            Slog.w(TAG, "No BOOTCLASSPATH found!");
        }

        if (systemServerClassPath != null) {
            String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
            for (String element : systemServerClassPathElements) {
                alreadyDexOpted.add(element);
            }
        } else {
            Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
        }

上面代碼開始時(shí)解析packages.xml,該文件保存了系統(tǒng)內(nèi)安裝了的APK的信息,然后就是添加一些庫(kù)類到alreadyDexOpted這個(gè)List里面,目的是以后做dexopt的時(shí)候跳過這些不必要的優(yōu)化。這里說明一下packages.xml中的字段的保存位置(以下是在Settings.java中)。

package  -> mPackages(readPackageLPw()-->addPackageLPw()) //重點(diǎn)

permissions ->  mPermissions(readPermissionsLPw())

permission-trees -> mPermissionTrees(readPermissionsLPw())

shared-user -> mSharedUsers(readSharedUserLPw()-->addSharedUserLPw())

updated-package -> mDisabledSysPackages(readDisabledSysPackageLPw()) //這個(gè)標(biāo)簽是在OTA中添加的?刪除也會(huì)有這個(gè)標(biāo)記?

renamed-package -> mRenamedPackages

這里強(qiáng)調(diào)一下,會(huì)把package的信息存在Settings.mPackages中,并根據(jù)package標(biāo)簽下的installStatus字段判斷app安裝的狀態(tài),這在后面有用到。下面繼續(xù)看PMS的構(gòu)造函數(shù)。

        //通過命令getprop ro.product.cpu.abilist查看設(shè)備支持的指令集
        final List<String> allInstructionSets = InstructionSets.getAllInstructionSets();
        final String[] dexCodeInstructionSets =
                getDexCodeInstructionSets(
                        allInstructionSets.toArray(new String[allInstructionSets.size()]));

        /**
         * Ensure all external libraries have had dexopt run on them.
         */
        if (mSharedLibraries.size() > 0) {
            // NOTE: For now, we're compiling these system "shared libraries"
            // (and framework jars) into all available architectures. It's possible
            // to compile them only when we come across an app that uses them (there's
            // already logic for that in scanPackageLI) but that adds some complexity.
            for (String dexCodeInstructionSet : dexCodeInstructionSets) {
                for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {
                    final String lib = libEntry.path;
                    if (lib == null) {
                        continue;
                    }

                    try {
                        int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
                        if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                            alreadyDexOpted.add(lib);
                            mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
                        }
                    } catch (FileNotFoundException e) {
                        Slog.w(TAG, "Library not found: " + lib);
                    } catch (IOException e) {
                        Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
                                + e.getMessage());
                    }
                }
            }
        }

這段代碼主要就是執(zhí)行dexopt的過程,并將優(yōu)化過的apk/jar放入alreadyDexOpted,這里mInstaller內(nèi)部通過LocalStock發(fā)送一條指令dexopt apkpath ....,讓LocalStock的服務(wù)端處理這個(gè)請(qǐng)求,我沒有找到服務(wù)端的實(shí)現(xiàn)位置....但不影響我們繼續(xù)閱讀。

        File frameworkDir = new File(Environment.getRootDirectory(), "framework");

        // Gross hack for now: we know this file doesn't contain any
        // code, so don't dexopt it to avoid the resulting log spew.
        alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");

        // Gross hack for now: we know this file is only part of
        // the boot class path for art, so don't dexopt it to
        // avoid the resulting log spew.
        alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");

        /**
         * There are a number of commands implemented in Java, which
         * we currently need to do the dexopt on so that they can be
         * run from a non-root shell.
         */
        String[] frameworkFiles = frameworkDir.list();
        if (frameworkFiles != null) {
            // TODO: We could compile these only for the most preferred ABI. We should
            // first double check that the dex files for these commands are not referenced
            // by other system apps.
            for (String dexCodeInstructionSet : dexCodeInstructionSets) {
                for (int i=0; i<frameworkFiles.length; i++) {
                    File libPath = new File(frameworkDir, frameworkFiles[i]);
                    String path = libPath.getPath();
                    // Skip the file if we already did it.
                    if (alreadyDexOpted.contains(path)) {
                        continue;
                    }
                    // Skip the file if it is not a type we want to dexopt.
                    if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
                        continue;
                    }
                    try {
                        int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
                        if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                            mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
                        }
                    } catch (FileNotFoundException e) {
                        Slog.w(TAG, "Jar not found: " + path);
                    } catch (IOException e) {
                        Slog.w(TAG, "Exception reading jar: " + path, e);
                    }
                }
            }
        }

這段代碼和上面的實(shí)現(xiàn)幾乎一樣,讀取/system/framework/下的幾個(gè)目錄的jar包和apk,判斷是否需要進(jìn)行優(yōu)化,值得注意的是并沒有加入列表alreadyDexOpted 。

        final VersionInfo ver = mSettings.getInternalVersion();
        mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
        // when upgrading from pre-M, promote system app permissions from install to runtime  運(yùn)行時(shí)權(quán)限
        mPromoteSystemApps =
                mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;

        // save off the names of pre-existing system packages prior to scanning; we don't
        // want to automatically grant runtime permissions for new system apps
        if (mPromoteSystemApps) {
            Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
            while (pkgSettingIter.hasNext()) {
                PackageSetting ps = pkgSettingIter.next();
                if (isSystemApp(ps)) {
                    mExistingSystemPackages.add(ps.name);
                }
            }
        }

當(dāng)系統(tǒng)有LOLLIPOP_MR1一下升級(jí)時(shí)到LOLLIPOP_MR1以上時(shí),android在6.0引入動(dòng)態(tài)權(quán)限機(jī)制,在這里也就是mPromoteSystemApps=true,會(huì)執(zhí)行下面一段代碼。

        // Collect vendor overlay packages.
        // (Do this before scanning any apps.)
        // For security and version matching reason, only consider
        // overlay packages if they reside in VENDOR_OVERLAY_DIR.
        File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);// = "/vendor/overlay"
        scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
                | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);

        // Find base frameworks (resource packages without code).
        scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM   //system/framework
                | PackageParser.PARSE_IS_SYSTEM_DIR
                | PackageParser.PARSE_IS_PRIVILEGED,
                scanFlags | SCAN_NO_DEX, 0);

        // Collected privileged system packages.
        final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
        scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
                | PackageParser.PARSE_IS_SYSTEM_DIR
                | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);

        // Collect ordinary system packages.
        final File systemAppDir = new File(Environment.getRootDirectory(), "app");
        scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
                | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

        // Collect all vendor packages.
        File vendorAppDir = new File("/vendor/app");
        try {
            vendorAppDir = vendorAppDir.getCanonicalFile();
        } catch (IOException e) {
            // failed to look up canonical path, continue with original one
        }
        scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
                | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

        // Collect all OEM packages.
        final File oemAppDir = new File(Environment.getOemDirectory(), "app");
        scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
                | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

        if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
        mInstaller.moveFiles(); //執(zhí)行LocalStock發(fā)送movefiles命令

上面的代碼的邏輯就是掃描指定的目錄,這里的目錄包括下面這些

/vendor/overlay
/system/framework
/system/priv-app
/system/app
/vendor/app
/oem/app

也就是我們上文提到的系統(tǒng)APK的存放目錄,掃描結(jié)束之后會(huì)把a(bǔ)pk信息存放在mPackages(這里是PMS,區(qū)別于SettingsmPackages)。下面我們來分析scanDirLI()的過程。

    private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
        final File[] files = dir.listFiles();
        if (ArrayUtils.isEmpty(files)) {
            Log.d(TAG, "No files in app dir " + dir);
            return;
        }

        if (DEBUG_PACKAGE_SCANNING) {
            Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
                    + " flags=0x" + Integer.toHexString(parseFlags));
        }
        //遍歷該目錄下的APK
        for (File file : files) {
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
            if (!isPackage) {
                // Ignore entries which are not packages
                continue;
            }
            try {
                scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                        scanFlags, currentTime, null);
            } catch (PackageManagerException e) {
                Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());

                // Delete invalid userdata apps
                // 刪除無效的用戶APK
                if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                        e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
                    logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
                    if (file.isDirectory()) {
                        mInstaller.rmPackageDir(file.getAbsolutePath());
                    } else {
                        file.delete();
                    }
                }
            }
        }
    }

上面代碼核心就是遍歷指定的文件夾,對(duì)文件夾內(nèi)部的文件執(zhí)行函數(shù)scanPackageLI(File,...) ,通過其注釋我們了解到他是掃描包的。它的代碼也比較長(zhǎng),下面選擇其中一部分說明。

    private PackageParser.Package scanPackageLI(File scanFile,  
            int parseFlags, int scanMode, long currentTime) {  
        ......  
  
        String scanPath = scanFile.getPath();  
        parseFlags |= mDefParseFlags;  
        PackageParser pp = new PackageParser();  
          
        ......  
  
        final PackageParser.Package pkg = pp.parsePackage(scanFile,  
            scanPath, mMetrics, parseFlags);  
  
        ......  
  
        return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime);  
    }  

為指定的文件創(chuàng)建PackageParser,將解析結(jié)果存入Package ,最后在調(diào)用函數(shù)scanPackageLI(Package,...)。而在函數(shù)PackageParser.Package.parsePackage(...)中會(huì)判斷scanFile是文件還是目錄(Android分包),會(huì)對(duì)他們做不同的處理,我們這里簡(jiǎn)單點(diǎn),就看是文件的分支,當(dāng)時(shí)文件時(shí),會(huì)調(diào)用函數(shù)parseMonolithicPackage(packageFile, flags) ,下面分析這個(gè)函數(shù)。

   public Package parseMonolithicPackage(File apkFile, int flags){ 
      final AssetManager assets = new AssetManager();
      final Package pkg = parseBaseApk(apkFile, assets, flags);
      pkg.codePath = apkFile.getAbsolutePath();
      return pkg;
   }  

這里的核心就是函數(shù)parseBaseApk(File,...),根據(jù)名稱感覺有些明朗了,不就解析APK嘛,看看到底是怎么實(shí)現(xiàn)的吧。

   private Package parseBaseApk(File apkFile, AssetManager assets, int flags){
        ....
        Resources res = null;
        XmlResourceParser parser = null;
        res = new Resources(assets, mMetrics, null);
        assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                    Build.VERSION.RESOURCES_SDK_INT);
        parser = assets.openXmlResourceParser(cookie, "AndroidManifest.xml");
        final String[] outError = new String[1];
        final Package pkg = parseBaseApk(res, parser, flags, outError);   
        ...
        return pkg;  
   }

臥槽,又是圈套,有調(diào)函數(shù)parseBaseApk(Resources,...)來解析,不過上面我們已經(jīng)看見關(guān)鍵的AndroidManifest.xml已經(jīng)出現(xiàn)了。通過閱讀parseBaseApk(Resources,...),我們發(fā)現(xiàn)他會(huì)解析AndroidManifest.xml中的一部分文件,這里大體包括以下標(biāo)簽

- application
- overlay
- key-sets
- permission-group
- permission-tree
- uses-permission
- uses-permission-sdk-m | uses-permission-sdk-23
- uses-configuration
- uses-feature
- feature-group
- uses-sdk
- supports-screens
- protected-broadcast
- instrumentation
- original-package
- adopt-permissions
- uses-gl-texture
- compatible-screens
- supports-input
- eat-comment

慚愧,好多標(biāo)簽沒見過,查看官網(wǎng)發(fā)現(xiàn)官網(wǎng)并沒有列舉以上全部AndroidManifest 。我們這里繼續(xù)跟進(jìn)application標(biāo)簽,發(fā)現(xiàn)他調(diào)用函數(shù)parseBaseApplication()。這個(gè)函數(shù)就是對(duì)Application內(nèi)部四大組件進(jìn)行解析。我們這里選取activity的部分來看看。

            // 函數(shù)參數(shù) Package owner
            if (tagName.equals("activity")) {
                //class Activity extends Component<ActivityIntentInfo>
                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
                        owner.baseHardwareAccelerated);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.activities.add(a);
            }

parseActivity()就是解析activity標(biāo)簽下的內(nèi)容,比如ActivityTheme什么的,解析的過程主要是利用TypedArray,具體的屬性可以看看Activity,很多屬性都是存儲(chǔ)在一個(gè)flags標(biāo)記了,這樣減少了類中成員字段過多,這在Android中使用的比較多,比如View中很多屬性也是存在一個(gè)flags中。到此一個(gè)解析好了的Package就好了,不知不覺,已經(jīng)偏了十萬八千里,不要急,拉回來,上面我們講到scanPackageLI(File,...)的最后調(diào)用了scanPackageLI(Package,...),那么這個(gè)函數(shù)有是做什么的呢?這個(gè)函數(shù)調(diào)用了scanPackageDirtyLI(),這個(gè)函數(shù)的代碼量也是相當(dāng)嚇人,這里不打算具體分析,主要工作就是為app創(chuàng)建目錄,也就是/data/data/apk.name/這個(gè)目錄,還有就是APK對(duì)應(yīng)的libs的存放位置,App簽名驗(yàn)證,收集APK要的權(quán)限,最重要的就是把解析信息存放到了PMSmPackages變量中,意味著App安裝成功了,后面會(huì)用到這個(gè)變量?;氐?code>PackageManagerService的構(gòu)造函數(shù)中來。

        // Prune any system packages that no longer exist.
        final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
        if (!mOnlyCore) {
            //mSettings.mPackages來自與package.xml的package標(biāo)簽
            Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
            while (psit.hasNext()) {
                PackageSetting ps = psit.next();

                /*
                 * If this is not a system app, it can't be a
                 * disable system app.
                 */
                if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
                    continue;
                }

                /*
                 * If the package is scanned, it's not erased.
                 */
                //PMS的mPackages存放掃描過的APK
                final PackageParser.Package scannedPkg = mPackages.get(ps.name);
                //掃描到了 && packages.xml中存在
                if (scannedPkg != null) {
                    /*
                     * If the system app is both scanned and in the
                     * disabled packages list, then it must have been
                     * added via OTA. Remove it from the currently
                     * scanned package so the previously user-installed
                     * application can be scanned.
                     */
                    //package.xml的package標(biāo)簽和updated-package標(biāo)簽都包含這個(gè)pkg
                    //根據(jù)上面注釋,意味著這個(gè)APK是通過OTA添加的,暫時(shí)移除
                    if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
                        logCriticalInfo(Log.WARN, "Expecting better updated system app for "
                                + ps.name + "; removing system app.  Last known codePath="
                                + ps.codePathString + ", installStatus=" + ps.installStatus
                                + ", versionCode=" + ps.versionCode + "; scanned versionCode="
                                + scannedPkg.mVersionCode);
                        removePackageLI(ps, true);//mPackages.remove(ps.name);
                        mExpectingBetter.put(ps.name, ps.codePath);
                    }

                    continue;
                }
                //沒有掃描到,在package標(biāo)簽下,但不在updated-package標(biāo)簽下,說明該APP已經(jīng)不存在了
                //因此要?jiǎng)h掉他的目錄
                //直接從mSettings.mPackages中移除
                if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
                    psit.remove(); 
                    logCriticalInfo(Log.WARN, "System package " + ps.name
                            + " no longer exists; wiping its data");
                    removeDataDirsLI(null, ps.name);
                } else {
                    //沒有掃描到,在package標(biāo)簽下,也在updated-package標(biāo)簽下
                    //可能由OTA引入(或刪除?)
                    final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
                    if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
                        possiblyDeletedUpdatedSystemApps.add(ps.name);
                    }
                }
            }
        }

處理被用戶隱藏的APP(前面講的pm hide package),因?yàn)楸浑[藏的APP在package.xml還存在,這里就是把這些APP從保存他們的列表中移除。另外就是在package.xml中還有該APK,但是掃描系統(tǒng)目錄發(fā)現(xiàn)這個(gè)APK已經(jīng)不存在了的處理方式。執(zhí)行完之后mSetting.mPackages剩下的就是無效的APK,我們需要將這些清除,于是就有了下面的幾行代碼

        //look for any incomplete package installations
        ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();//獲取mSettings.mPackages中installStatus為未成功安裝的App(前文有講,package中installStatus=false的apk)
        //clean up list
        for(int i = 0; i < deletePkgsList.size(); i++) {
            //clean up here
            cleanupInstallFailedPackage(deletePkgsList.get(i));
        }
        //delete tmp files
        deleteTempPackageFiles();

        // Remove any shared userIDs that have no associated packages
        mSettings.pruneSharedUsersLPw();//移除沒有被關(guān)聯(lián)的mSharedUsers

上面的作用就是清除無效APK引入的文件夾等。系統(tǒng)APK裝載完了,下面就開始裝載用戶APK,

            scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
            scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
                    scanFlags | SCAN_REQUIRE_KNOWN, 0);

上面的代碼和前面掃描系統(tǒng)APK是一樣的,這是目錄和flags變了,邏輯是一樣的。這里的目錄包括

/data/app
/data/app-private

繼續(xù)看構(gòu)造函數(shù)。

            /**
             * Remove disable package settings for any updated system
             * apps that were removed via an OTA. If they're not a
             * previously-updated app, remove them completely.
             * Otherwise, just revoke their system-level permissions.
             */
            //在引進(jìn)了用戶app之后mPackages內(nèi)容增加了,再看看是否有這些app
            for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
                PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
                mSettings.removeDisabledSystemPackageLPw(deletedAppName);

                String msg;
                if (deletedPkg == null) {  //OTA刪除
                    msg = "Updated system package " + deletedAppName
                            + " no longer exists; wiping its data";
                    removeDataDirsLI(null, deletedAppName);
                } else {    //在用戶app中找到了,當(dāng)然會(huì)移除系統(tǒng)包標(biāo)識(shí)
                    msg = "Updated system app + " + deletedAppName
                            + " no longer present; removing system privileges for "
                            + deletedAppName;

                    deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;

                    PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
                    deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
                }
                logCriticalInfo(Log.WARN, msg);
            }

            /**
             * Make sure all system apps that we expected to appear on
             * the userdata partition actually showed up. If they never
             * appeared, crawl back and revive the system version.
             */
            for (int i = 0; i < mExpectingBetter.size(); i++) {  //有新包,更新APK
                final String packageName = mExpectingBetter.keyAt(i);
                if (!mPackages.containsKey(packageName)) {
                    final File scanFile = mExpectingBetter.valueAt(i);

                    logCriticalInfo(Log.WARN, "Expected better " + packageName
                            + " but never showed up; reverting to system");

                    final int reparseFlags;
                    //不同目錄flags不一樣
                    if (FileUtils.contains(privilegedAppDir, scanFile)) {
                        reparseFlags = PackageParser.PARSE_IS_SYSTEM
                                | PackageParser.PARSE_IS_SYSTEM_DIR
                                | PackageParser.PARSE_IS_PRIVILEGED;
                    } else if (FileUtils.contains(systemAppDir, scanFile)) {
                        reparseFlags = PackageParser.PARSE_IS_SYSTEM
                                | PackageParser.PARSE_IS_SYSTEM_DIR;
                    } else if (FileUtils.contains(vendorAppDir, scanFile)) {
                        reparseFlags = PackageParser.PARSE_IS_SYSTEM
                                | PackageParser.PARSE_IS_SYSTEM_DIR;
                    } else if (FileUtils.contains(oemAppDir, scanFile)) {
                        reparseFlags = PackageParser.PARSE_IS_SYSTEM
                                | PackageParser.PARSE_IS_SYSTEM_DIR;
                    } else {
                        Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
                        continue;
                    }
                    //加入mStting.mPackages   
                    mSettings.enableSystemPackageLPw(packageName);

                    try {
                        scanPackageLI(scanFile, reparseFlags, scanFlags, 0, null);
                    } catch (PackageManagerException e) {
                        Slog.e(TAG, "Failed to parse original system package: "
                                + e.getMessage());
                    }
                }
            }
            mExpectingBetter.clear();

上面這段代碼就是刪除被OTA移除app的目錄,更新新引入的App的目錄。

        // Now that we know all of the shared libraries, update all clients to have
        // the correct library paths.
        updateAllSharedLibrariesLPw(); //為需要sharelibs的apk關(guān)聯(lián)libs,放在pkg.usesLibraryFiles

        for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
            // NOTE: We ignore potential failures here during a system scan (like
            // the rest of the commands above) because there's precious little we
            // can do about it. A settings error is reported, though.
            adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
                    false /* force dexopt */, false /* defer dexopt */);
        }

        // Now that we know all the packages we are keeping,
        // read and update their last usage times.
        mPackageUsage.readLP();//讀/data/system/package-usage.list

        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.
        int updateFlags = UPDATE_PERMISSIONS_ALL;
        if (ver.sdkVersion != mSdkVersion) {
            Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to "
                    + mSdkVersion + "; regranting permissions for internal storage");
            updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
        }
        updatePermissionsLPw(null, null, updateFlags);//Apk分配權(quán)限
        ver.sdkVersion = mSdkVersion;
        // clear only after permissions have been updated
        mExistingSystemPackages.clear();
        mPromoteSystemApps = false;

        // If this is the first boot, and it is a normal boot, then
        // we need to initialize the default preferred apps.
        //第一次啟動(dòng),初始化默認(rèn)程序,如瀏覽器,email程序
        if (!mRestoredSettings && !onlyCore) {
            mSettings.applyDefaultPreferredAppsLPw(this, UserHandle.USER_OWNER);
            applyFactoryDefaultBrowserLPw(UserHandle.USER_OWNER);
            primeDomainVerificationsLPw(UserHandle.USER_OWNER);
        }

        // If this is first boot after an OTA, and a normal boot, then
        // we need to clear code cache directories.
        if (mIsUpgrade && !onlyCore) {
            Slog.i(TAG, "Build fingerprint changed; clearing code caches");
            for (int i = 0; i < mSettings.mPackages.size(); i++) {
                final PackageSetting ps = mSettings.mPackages.valueAt(i);
                if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
                    deleteCodeCacheDirsLI(ps.volumeUuid, ps.name);
                }
            }
            ver.fingerprint = Build.FINGERPRINT;
        }

        checkDefaultBrowser();

        // All the changes are done during package scanning.
        ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;

        // can downgrade to reader
        mSettings.writeLPr(); //寫package.xml

        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                SystemClock.uptimeMillis());

        mRequiredVerifierPackage = getRequiredVerifierLPr(); //string
        mRequiredInstallerPackage = getRequiredInstallerLPr(); //string

        mInstallerService = new PackageInstallerService(context, this); //根據(jù)名字知道大概是app安裝相關(guān)服務(wù)

        mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
        mIntentFilterVerifier = new IntentVerifierProxy(mContext,
                mIntentFilterVerifierComponent);

    } // synchronized (mPackages)
    } // synchronized (mInstallLock)

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

    // Expose private service for system components to use.
    LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
}

到此,PMS的構(gòu)造函數(shù)就閱讀完畢了。

總結(jié)

本文由pm命令引入,講解PMS的構(gòu)造過程,這里在梳理一下這個(gè)過程。

  1. 通過packages.xml(backup)讀取系統(tǒng)已經(jīng)安裝了的app,保存到mSettings的相關(guān)字段中去。
  2. 通過掃描系統(tǒng)目錄/用戶app目錄,將掃描的app保存到PMSmPackages中去。
  3. 根據(jù)上兩步的結(jié)果判斷哪些app無效(刪除),哪些被OTA的方式更新,刪除或重建對(duì)應(yīng)app的目錄
  4. app掃描結(jié)束,app權(quán)限分配,app引用的庫(kù)的關(guān)聯(lián)
  5. 重新保存這些新信息到packages.xml供下次開機(jī)使用

參考

廣告

關(guān)注我的微信公眾號(hào),及時(shí)獲得博文推送
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容