Framework基础:包管理之system/priv-app下的子目录规则

《Framework基础:包管理之system/priv-app下的子目录规则》 Paste_Image.png

今天忙的要命。今天发年终奖了,第一次拿年终奖,感觉比想象中多一点。今天主要在同步一堆的代码,现在改一个代码可麻烦了,要同步到很多个平台,高通mtk平台全部同步一篇,真是同步到手软,不知道其他公司是不是这样做的,小米他们几十台手机,项目不知道是怎么管理的。说回正题,今天主要处理一个运营商的一个要求,运营商要求把他的app内置到system/priv-app,但只在运营商版本把这个apk安装,手机有一个标志位区分运营商版本和普通版本,如何处理呢?

将运营商apk放置到system/priv-app的子目录是否可以呢?

在system/priv-app新建一个子目录Carrier,然后把该运营商的所有apk放置到这个这个目录。有个子目录,就可以便于区分运营商的apk和系统的apk了。主要是比较好看,如果直接把apk都扔到system/priv-app,那system/priv-app下的apk就太多了,看花眼了。放置完就是下面这样子。

《Framework基础:包管理之system/priv-app下的子目录规则》 内置apk.png

但实践发现,这样子apk并不能安装。

为什么呢?还是从源码出发,看看安装的流程。
/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
在PackageManagerService的构造函数中,会去扫描system/priv-app 目录

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

进入scanDirLI

    private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
        final File[] files = dir.listFiles();
        for (File file : files) {
               //扫到运营商目录是,file的值是System/priv-app/Carrier
                scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
        }
    }

进入scanPackageLI

    private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
            long currentTime, UserHandle user) throws PackageManagerException {
        .................
       PackageParser pp = new PackageParser();
       final PackageParser.Package pkg;
        try {
            //解释System/priv-app/Carrier文件夹
            pkg = pp.parsePackage(scanFile, parseFlags);
        } catch (PackageParserException e) {
            throw PackageManagerException.from(e);
        }
        ................
    }

PackageParser用于解释文件夹下的apk文件,把解释结果放在PackageParser.Package中。
看看PackageParser的parsePackage方法

    public Package parsePackage(File packageFile, int flags) throws PackageParserException {
        if (packageFile.isDirectory()) {   //System/priv-app/Carrier满足这个条件
            return parseClusterPackage(packageFile, flags);
        } else {
            return parseMonolithicPackage(packageFile, flags);
        }
    }

    private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
       ......................
        final PackageLite lite = parseClusterPackageLite(packageDir, 0);
        .......................
    }

    private static PackageLite parseClusterPackageLite(File packageDir, int flags)
            throws PackageParserException {
        ..............
        final File[] files = packageDir.listFiles();

        final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
        for (File file : files) {
            //列出System/priv-app/Carrier所有apk,包括hehe.apk,haha.apk
            if (isApkFile(file)) {
                final ApkLite lite = parseApkLite(file, flags);

                // Assert that all package names and version codes are
                // consistent with the first one we encounter.
                if (packageName == null) {
                    packageName = lite.packageName;  //
                    versionCode = lite.versionCode;
                } else {
                    //关键点在这里,如果两个apk不一样,就会抛出异常。说明子目录只能有一个apk
                    if (!packageName.equals(lite.packageName)) {
                        throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                                "Inconsistent package " + lite.packageName + " in " + file
                                + "; expected " + packageName);
                    }
                    if (versionCode != lite.versionCode) {
                        throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                                "Inconsistent version " + lite.versionCode + " in " + file
                                + "; expected " + versionCode);
                    }
                }

                // Assert that each split is defined only once
                if (apks.put(lite.splitName, lite) != null) {
                    throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                            "Split name " + lite.splitName
                            + " defined more than once; most recent was " + file);
                }
            }
        }
        ...................
    }

看看上面的注释,pms会判断system/priv-app子目录的apk包名,要保证子目录只有一个apk,否则不能安装。因为System/priv-app/Carrier有两个apk,hehe.apk和haha.apk,所以安装失败!!分析完毕。看看下面正常的apk都是长这样的,子目录只有一个apk

《Framework基础:包管理之system/priv-app下的子目录规则》 一个apk.png

总结

system/priv-app下的子目录内只能有一个apk,否则会安装失败。

    原文作者:九九叔
    原文地址: https://www.jianshu.com/p/cf1370b1ad4e
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞