Android-包管理框架—PKMS

364 阅读3分钟

服务启动

PKMS 服务在 SystemServer.java 的 startBootstrapServices() 中启动;

    t.traceBegin("StartPackageManagerService");
    try {
        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
    }
    t.traceEnd();

服务启动处理主要在 PackageManagerService 的 main() 中,其创建了 Settings 和 PackageManagerService 对象,并把服务注册到 ServiceManager 中;

    public static PackageManagerService main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        t.traceBegin("create package manager");
        //创建Injector对象用于PKMS的运行时环境
        Injector injector = new Injector(
              context, lock, installer, installLock, new PackageAbiHelperImpl(),
              (i, pm) -> new Settings(Environment.getDataDirectory(),
                                      i.getPermissionManagerServiceInternal()
                                      .getPermissionSettings(), lock),
              (i, pm) -> (PlatformCompat) ServiceManager
                          .getService("platform_compat"));
    
        // PKMS构造方法,是整个PKMS启动过程的核心
        PackageManagerService m = new PackageManagerService(
                                      injector, onlyCore, factoryTest);
        t.traceEnd(); // "create package manager"
    
        // 根据用户类型为所有用户安装/卸载系统包
        m.installWhitelistedSystemPackages();
        // 注册服务
        ServiceManager.addService("package", m);
        final PackageManagerNative pmn = m.new PackageManagerNative();
        ServiceManager.addService("package_native", pmn);
        return m;
    }

Settings 对象,主要用来保存有关动态设置的信息,在其构造方法中初始化了几个 File 类,这几个文件主要保存包管理信息;

    Settings(File dataDir, PermissionSettings permission, Object lock) {
        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, 0640, SYSTEM_UID, 
                                 PACKAGE_INFO_GID);
    
        final File kernelDir = new File("/config/sdcardfs");
        mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;
    
        // Deprecated: Needed for migration
        mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
        mBackupStoppedPackagesFilename = new File(mSystemDir, 
                                                  "packages-stopped-backup.xml");
    }

扫描 APK

PKMS 的构造函数主要是对配置文件进行读取,扫描系统中的 APK 并写入到内存或者文件中;

    public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
        mSettings = injector.getSettings();
        t.traceBegin("addSharedUsers");
        // 初始化一些 SharedUser 的 name 和 SharedUserSetting 的一一对应关系
        // 如对于 name 为 android.uid.system 的 SharedUser,其对应的
        // SharedUserSetting(pkgFlags :ApplicationInfo.FLAG_SYSTEM
        // pkgPrivateFlags :ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
        // uid :Process.SYSTEM_UID)
        mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                ApplicationInfo.FLAG_SYSTEM, 
                ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        t.traceEnd();
    
        // CHECKSTYLE:OFF IndentationCheck
        synchronized (mInstallLock) {
        // writer
        synchronized (mLock) {
            t.traceBegin("read user settings");
            // 使用 PullParser 方式读取 packages.xml 或 packages-backup.xml,
            // 解析文件节点生成对应的对象并保存
            mFirstBoot = !mSettings.readLPw(
                    mInjector.getUserManagerInternal().getUsers(
                    /* excludePartial= */ true,
                    /* excludeDying= */ false,
                    /* excludePreCreated= */ false));
            t.traceEnd();
    
            File frameworkDir = new File(Environment.getRootDirectory(), 
                                         "framework");
            mCacheDir = preparePackageParserCache();
            PackageParser2 packageParser = new PackageParser2(mSeparateProcesses, 
                    mOnlyCore, mMetrics, mCacheDir, mPackageParserCallback);
            ExecutorService executorService = ParallelPackageParser
                                                     .makeExecutorService();
            // Prepare apex package info before scanning APKs, these information are
            // needed when scanning apk in apex.
            // Apex 是用于系统安装包的升级
            mApexManager.scanApexPackagesTraced(packageParser, executorService);
            // 扫描 system/framework 目录下的 apk 文件
            scanDirTracedLI(frameworkDir, systemParseFlags,
                    systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
                    packageParser, executorService);
            for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
                final ScanPartition partition = mDirsToScanAsSystem.get(i);
                if (partition.getPrivAppFolder() != null) {
                    // 扫描 system/priv-app 目录下的 apk 文件
                    scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
                          systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,
                          0, packageParser, executorService);
                }
                // 扫描 system/app 目录下的 apk 文件
                scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
                      systemScanFlags | partition.scanFlag, 0,
                      packageParser, executorService);
            }
    
            if (!mOnlyCore) {
                // 扫描 data/data/app 目录下的 apk 文件
                scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
                        packageParser, executorService);
            }
            t.traceBegin("write settings");
            // 更新 packages.xml
            mSettings.writeLPr();
            t.traceEnd();
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                    SystemClock.uptimeMillis());
        } // synchronized (mLock)
        } // synchronized (mInstallLock)
        // CHECKSTYLE:ON IndentationCheck
    }

Apex 是在 Android10 中谷歌为了解决系统升级而提出的一个概念。和 APK 类似,Apex 把 Framework 层中关键的功能搞成一个个的模块,然后可以单独升级这些模块。 这些模块就和一个一个的 APK 类似,就是一个压缩包,后缀名叫 .apex。

Apex 和 Apk 的区别:

  • apk 是应用程序的载体,对应用开发者而言,可以 apk 方式对应用功能进行升级。
  • apex 是系统功能的载体,对系统开发者(目前看主要是谷歌)而言,可以 apex 方式对系统功能进行升级。 apex 相当于对系统功能进行了更细粒度的划分,可以独立升级这些功能,这些 apex 包将来就发布在谷歌的 playstore 上供我们下载。

scanDirTracedLI() 的主要是扫描 APK 的处理,它又继续调用了 scanDirLI(),大体代码如下:

    private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
        final File[] files = scanDir.listFiles();
        // 创建一个ParallelPackageParser用于解析操作,其内部会使用线程池进行处理
        ParallelPackageParser parallelPackageParser =
                new ParallelPackageParser(packageParser, executorService);
    
        // Submit files for parsing in parallel
        int fileCount = 0;
        for (File file : files) {
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
            if (!isPackage) {
                // Ignore entries which are not packages
                continue;
            }
            // 提交安装包文件以进行解析并保存信息
            // 这里的包可能是单独的APK文件,也可能是APK目录
            // 若是APK目录,则会依次解析目录中包含的APK文件
            parallelPackageParser.submit(file, parseFlags);
            fileCount++;
        }
    
        // Process results one by one
        for (; fileCount > 0; fileCount--) {
            // 从解析队列中取出已解析的包信息
            ParallelPackageParser.ParseResult parseResult = 
                              parallelPackageParser.take();
            Throwable throwable = parseResult.throwable;
            if (throwable == null) {
                try {
                    // 将前面解析到的Package信息,更新到系统中,
                    // 并更新某些系统文件信息,且对满足版本要求的应用进行更新操作
                    addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
                            currentTime, null);
                }
            }
        }
    }

PKMS 系统服务的启动大体流程便如此,启动后用户便可与底层交互,实现应用的安装、卸载、包信息获取等功能。